From 132ad1fbee7c0b6544274b4163ed769e1af14ef4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 6 Jan 2020 19:37:28 +0000 Subject: [PATCH 01/11] update to xen-4.12.2 --- .gitignore | 2 +- sources | 2 +- ...32f0d815bee3102ede328676e1a4ca1926c7.patch | 341 ---------- ...ecb9991bf2d1ddb933872c3dfbd26300ca68.patch | 110 ---- xen.spec | 82 +-- xsa296.patch | 195 ------ xsa298.patch | 89 --- ...F-checks-don-t-leave-a-partial-entry.patch | 94 --- ...set-PGT_pinned-on-a-partially-de-val.patch | 99 --- ...out-partial_pte-tristate-into-indivi.patch | 618 ------------------ ...-for-_put_page_type-rather-than-a-bo.patch | 140 ---- ...t_page_and_type_from_mfn-conditional.patch | 79 --- ...c_l-23-_table-clear-partial_flags-wh.patch | 111 ---- ...ways-retain-a-general-ref-on-partial.patch | 378 ----------- ...PTF_partial_set-and-PTF_partial_gene.patch | 227 ------- ...handle-linear-pagetable-promotion-fa.patch | 106 --- ...mm-Fix-nested-de-validation-on-error.patch | 166 ----- ...p-a-type-ref-unless-you-held-a-ref-t.patch | 413 ------------ xsa301-master-1.patch | 80 --- xsa301-master-2.patch | 92 --- xsa301-master-3.patch | 67 -- ....12-0001-IOMMU-add-missing-HVM-check.patch | 37 -- ...2-passthrough-quarantine-PCI-devices.patch | 499 -------------- ...try-Split-__DEFINE_ENTRY_TRAP-in-two.patch | 74 --- ...Fold-the-macro-SAVE_ALL-in-the-macro.patch | 97 --- ...blindly-unmask-interrupts-on-trap-wi.patch | 226 ------- ...blindly-unmask-interrupts-on-trap-wi.patch | 114 ---- xsa304-4.12-1.patch | 71 -- xsa304-4.12-2.patch | 272 -------- xsa304-4.12-3.patch | 108 --- xsa305-4.12-1.patch | 288 -------- xsa305-4.12-2.patch | 192 ------ xsa306-4.12.patch | 91 --- xsa307.patch | 99 --- xsa308.patch | 74 --- xsa309.patch | 58 -- ...uest_table-when-destroying-vcpu-page.patch | 167 ----- ...e_lN_table-Retain-partial_flags-on-E.patch | 104 --- ...h_memory-Grab-an-extra-type-ref-when.patch | 75 --- xsa311-4.12.patch | 194 ------ 40 files changed, 8 insertions(+), 6323 deletions(-) delete mode 100644 xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch delete mode 100644 xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch delete mode 100644 xsa296.patch delete mode 100644 xsa298.patch delete mode 100644 xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch delete mode 100644 xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch delete mode 100644 xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch delete mode 100644 xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch delete mode 100644 xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch delete mode 100644 xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch delete mode 100644 xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch delete mode 100644 xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch delete mode 100644 xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch delete mode 100644 xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch delete mode 100644 xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch delete mode 100644 xsa301-master-1.patch delete mode 100644 xsa301-master-2.patch delete mode 100644 xsa301-master-3.patch delete mode 100644 xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch delete mode 100644 xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch delete mode 100644 xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch delete mode 100644 xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch delete mode 100644 xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch delete mode 100644 xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch delete mode 100644 xsa304-4.12-1.patch delete mode 100644 xsa304-4.12-2.patch delete mode 100644 xsa304-4.12-3.patch delete mode 100644 xsa305-4.12-1.patch delete mode 100644 xsa305-4.12-2.patch delete mode 100644 xsa306-4.12.patch delete mode 100644 xsa307.patch delete mode 100644 xsa308.patch delete mode 100644 xsa309.patch delete mode 100644 xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch delete mode 100644 xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch delete mode 100644 xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch delete mode 100644 xsa311-4.12.patch diff --git a/.gitignore b/.gitignore index 0c4dcc0..972c3fa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.12.1.tar.gz +/xen-4.12.2.tar.gz diff --git a/sources b/sources index 7f28202..63327d7 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.12.1.tar.gz) = be15d3af6b109771aaf59b3621e4e1560b7d9a963d9b7f6a1f6f4a1c907fd97434b060c15f75849b44fbda33f26eb51b030d14d068c6ad5103ad240fe7a98f40 +SHA512 (xen-4.12.2.tar.gz) = 7d9e7921271830c9eadf1bb8eca1aec20d343ad7475b0dc3165ef6d681759e7cb70739f8d9f85622a23aef960988820e822267fb198b12ee3dd657ad6164069f diff --git a/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch b/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch deleted file mode 100644 index edbd01a..0000000 --- a/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch +++ /dev/null @@ -1,341 +0,0 @@ -From 1e8932f0d815bee3102ede328676e1a4ca1926c7 Mon Sep 17 00:00:00 2001 -From: Paul Durrant -Date: Fri, 6 Dec 2019 12:46:24 +0100 -Subject: [PATCH] x86 / iommu: set up a scratch page in the quarantine domain - -This patch introduces a new iommu_op to facilitate a per-implementation -quarantine set up, and then further code for x86 implementations -(amd and vtd) to set up a read-only scratch page to serve as the source -for DMA reads whilst a device is assigned to dom_io. DMA writes will -continue to fault as before. - -The reason for doing this is that some hardware may continue to re-try -DMA (despite FLR) in the event of an error, or even BME being cleared, and -will fail to deal with DMA read faults gracefully. Having a scratch page -mapped will allow pending DMA reads to complete and thus such buggy -hardware will eventually be quiesced. - -NOTE: These modifications are restricted to x86 implementations only as - the buggy h/w I am aware of is only used with Xen in an x86 - environment. ARM may require similar code but, since I am not - aware of the need, this patch does not modify any ARM implementation. - -Signed-off-by: Paul Durrant -Reviewed-by: Jan Beulich -master commit: ea38867831da67eed0e9c61672c8941016b63dd9 -master date: 2019-11-29 18:27:54 +0000 ---- - xen/drivers/passthrough/amd/iommu_map.c | 62 ++++++++++++++++++++++ - xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++--- - xen/drivers/passthrough/iommu.c | 17 +++++- - xen/drivers/passthrough/vtd/iommu.c | 74 ++++++++++++++++++++++----- - xen/include/asm-x86/hvm/svm/amd-iommu-proto.h | 3 ++ - xen/include/xen/iommu.h | 1 + - 6 files changed, 147 insertions(+), 24 deletions(-) - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 628aa60230..f876ce832b 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -758,6 +758,68 @@ void amd_iommu_share_p2m(struct domain *d) - } - } - -+int __init amd_iommu_quarantine_init(struct domain *d) -+{ -+ struct domain_iommu *hd = dom_iommu(d); -+ unsigned long max_gfn = -+ PFN_DOWN((1ul << DEFAULT_DOMAIN_ADDRESS_WIDTH) - 1); -+ unsigned int level = amd_iommu_get_paging_mode(max_gfn); -+ uint64_t *table; -+ -+ if ( hd->arch.root_table ) -+ { -+ ASSERT_UNREACHABLE(); -+ return 0; -+ } -+ -+ spin_lock(&hd->arch.mapping_lock); -+ -+ hd->arch.root_table = alloc_amd_iommu_pgtable(); -+ if ( !hd->arch.root_table ) -+ goto out; -+ -+ table = __map_domain_page(hd->arch.root_table); -+ while ( level ) -+ { -+ struct page_info *pg; -+ unsigned int i; -+ -+ /* -+ * The pgtable allocator is fine for the leaf page, as well as -+ * page table pages, and the resulting allocations are always -+ * zeroed. -+ */ -+ pg = alloc_amd_iommu_pgtable(); -+ if ( !pg ) -+ break; -+ -+ for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) -+ { -+ uint32_t *pde = (uint32_t *)&table[i]; -+ -+ /* -+ * PDEs are essentially a subset of PTEs, so this function -+ * is fine to use even at the leaf. -+ */ -+ set_iommu_pde_present(pde, mfn_x(page_to_mfn(pg)), level - 1, -+ false, true); -+ } -+ -+ unmap_domain_page(table); -+ table = __map_domain_page(pg); -+ level--; -+ } -+ unmap_domain_page(table); -+ -+ out: -+ spin_unlock(&hd->arch.mapping_lock); -+ -+ amd_iommu_flush_all_pages(d); -+ -+ /* Pages leaked in failure case */ -+ return level ? -ENOMEM : 0; -+} -+ - /* - * Local variables: - * mode: C -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 15c13e1163..93ecae4a6c 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -120,10 +120,6 @@ static void amd_iommu_setup_domain_device( - u8 bus = pdev->bus; - const struct domain_iommu *hd = dom_iommu(domain); - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return; -- - BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || - !iommu->dev_table.buffer ); - -@@ -226,7 +222,7 @@ static int __must_check allocate_domain_resources(struct domain_iommu *hd) - return rc; - } - --static int get_paging_mode(unsigned long entries) -+int amd_iommu_get_paging_mode(unsigned long entries) - { - int level = 1; - -@@ -248,7 +244,8 @@ static int amd_iommu_domain_init(struct domain *d) - - /* For pv and dom0, stick with get_paging_mode(max_page) - * For HVM dom0, use 2 level page table at first */ -- hd->arch.paging_mode = is_hvm_domain(d) ? 2 : get_paging_mode(max_page); -+ hd->arch.paging_mode = is_hvm_domain(d) ? -+ 2 : amd_iommu_get_paging_mode(max_page); - return 0; - } - -@@ -281,10 +278,6 @@ void amd_iommu_disable_domain_device(struct domain *domain, - int req_id; - u8 bus = pdev->bus; - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return; -- - BUG_ON ( iommu->dev_table.buffer == NULL ); - req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); - dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); -@@ -581,6 +574,7 @@ static void amd_dump_p2m_table(struct domain *d) - static const struct iommu_ops __initconstrel amd_iommu_ops = { - .init = amd_iommu_domain_init, - .hwdom_init = amd_iommu_hwdom_init, -+ .quarantine_init = amd_iommu_quarantine_init, - .add_device = amd_iommu_add_device, - .remove_device = amd_iommu_remove_device, - .assign_device = amd_iommu_assign_device, -diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c -index 20db08e1df..a747bfb946 100644 ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -497,6 +497,21 @@ int iommu_iotlb_flush_all(struct domain *d, unsigned int flush_flags) - return rc; - } - -+static int __init iommu_quarantine_init(void) -+{ -+ const struct domain_iommu *hd = dom_iommu(dom_io); -+ int rc; -+ -+ rc = iommu_domain_init(dom_io); -+ if ( rc ) -+ return rc; -+ -+ if ( !hd->platform_ops->quarantine_init ) -+ return 0; -+ -+ return hd->platform_ops->quarantine_init(dom_io); -+} -+ - int __init iommu_setup(void) - { - int rc = -ENODEV; -@@ -530,7 +545,7 @@ int __init iommu_setup(void) - printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); - if ( iommu_enabled ) - { -- if ( iommu_domain_init(dom_io) ) -+ if ( iommu_quarantine_init() ) - panic("Could not set up quarantine\n"); - - printk(" - Dom0 mode: %s\n", -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 5663e9740d..576e72eba1 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -192,7 +192,7 @@ u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages) - nodeid_t node = NUMA_NO_NODE; - unsigned int i; - -- rhsa = drhd_to_rhsa(drhd); -+ rhsa = drhd ? drhd_to_rhsa(drhd) : NULL; - if ( rhsa ) - node = pxm_to_node(rhsa->proximity_domain); - -@@ -1340,10 +1340,6 @@ int domain_context_mapping_one( - int agaw, rc, ret; - bool_t flush_dev_iotlb; - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return 0; -- - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - maddr = bus_to_context_maddr(iommu, bus); -@@ -1579,10 +1575,6 @@ int domain_context_unmap_one( - int iommu_domid, rc, ret; - bool_t flush_dev_iotlb; - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return 0; -- - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - -@@ -1715,10 +1707,6 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - goto out; - } - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- goto out; -- - /* - * if no other devices under the same iommu owned by this domain, - * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp -@@ -2736,9 +2724,69 @@ static void vtd_dump_p2m_table(struct domain *d) - vtd_dump_p2m_table_level(hd->arch.pgd_maddr, agaw_to_level(hd->arch.agaw), 0, 0); - } - -+static int __init intel_iommu_quarantine_init(struct domain *d) -+{ -+ struct domain_iommu *hd = dom_iommu(d); -+ struct dma_pte *parent; -+ unsigned int agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); -+ unsigned int level = agaw_to_level(agaw); -+ int rc; -+ -+ if ( hd->arch.pgd_maddr ) -+ { -+ ASSERT_UNREACHABLE(); -+ return 0; -+ } -+ -+ spin_lock(&hd->arch.mapping_lock); -+ -+ hd->arch.pgd_maddr = alloc_pgtable_maddr(NULL, 1); -+ if ( !hd->arch.pgd_maddr ) -+ goto out; -+ -+ parent = map_vtd_domain_page(hd->arch.pgd_maddr); -+ while ( level ) -+ { -+ uint64_t maddr; -+ unsigned int offset; -+ -+ /* -+ * The pgtable allocator is fine for the leaf page, as well as -+ * page table pages, and the resulting allocations are always -+ * zeroed. -+ */ -+ maddr = alloc_pgtable_maddr(NULL, 1); -+ if ( !maddr ) -+ break; -+ -+ for ( offset = 0; offset < PTE_NUM; offset++ ) -+ { -+ struct dma_pte *pte = &parent[offset]; -+ -+ dma_set_pte_addr(*pte, maddr); -+ dma_set_pte_readable(*pte); -+ } -+ iommu_flush_cache_page(parent, 1); -+ -+ unmap_vtd_domain_page(parent); -+ parent = map_vtd_domain_page(maddr); -+ level--; -+ } -+ unmap_vtd_domain_page(parent); -+ -+ out: -+ spin_unlock(&hd->arch.mapping_lock); -+ -+ rc = iommu_flush_iotlb_all(d); -+ -+ /* Pages leaked in failure case */ -+ return level ? -ENOMEM : rc; -+} -+ - const struct iommu_ops __initconstrel intel_iommu_ops = { - .init = intel_iommu_domain_init, - .hwdom_init = intel_iommu_hwdom_init, -+ .quarantine_init = intel_iommu_quarantine_init, - .add_device = intel_iommu_add_device, - .enable_device = intel_iommu_enable_device, - .remove_device = intel_iommu_remove_device, -diff --git a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h -index c5697565d6..5a160b89ca 100644 ---- a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h -+++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h -@@ -51,6 +51,9 @@ void get_iommu_features(struct amd_iommu *iommu); - int amd_iommu_init(void); - int amd_iommu_update_ivrs_mapping_acpi(void); - -+int amd_iommu_get_paging_mode(unsigned long entries); -+int amd_iommu_quarantine_init(struct domain *d); -+ - /* mapping functions */ - int __must_check amd_iommu_map_page(struct domain *d, dfn_t dfn, - mfn_t mfn, unsigned int flags, -diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h -index 70ee53d083..617c993f50 100644 ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -189,6 +189,7 @@ typedef int iommu_grdm_t(xen_pfn_t start, xen_ulong_t nr, u32 id, void *ctxt); - struct iommu_ops { - int (*init)(struct domain *d); - void (*hwdom_init)(struct domain *d); -+ int (*quarantine_init)(struct domain *d); - int (*add_device)(u8 devfn, device_t *dev); - int (*enable_device)(device_t *dev); - int (*remove_device)(u8 devfn, device_t *dev); --- -2.11.0 - diff --git a/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch b/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch deleted file mode 100644 index 448871a..0000000 --- a/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 36d2ecb9991bf2d1ddb933872c3dfbd26300ca68 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Mon, 25 Nov 2019 16:01:00 +0100 -Subject: [PATCH] AMD/IOMMU: don't needlessly trigger errors/crashes when - unmapping a page - -Unmapping a page which has never been mapped should be a no-op (note how -it already is in case there was no root page table allocated). There's -in particular no need to grow the number of page table levels in use, -and there's also no need to allocate intermediate page tables except -when needing to split a large page. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Acked-by: Andrew Cooper -master commit: ad591454f069647c36a7daaa9ec23384c0263f0b -master date: 2019-11-12 11:08:34 +0100 ---- - xen/drivers/passthrough/amd/iommu_map.c | 40 +++++++++++++++------------------ - 1 file changed, 18 insertions(+), 22 deletions(-) - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 67329b0c95..628aa60230 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -343,7 +343,7 @@ uint64_t amd_iommu_get_address_from_pte(void *pte) - * page tables. - */ - static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, -- unsigned long pt_mfn[]) -+ unsigned long pt_mfn[], bool map) - { - uint64_t *pde, *next_table_vaddr; - unsigned long next_table_mfn; -@@ -356,6 +356,13 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - - BUG_ON( table == NULL || level < 1 || level > 6 ); - -+ /* -+ * A frame number past what the current page tables can represent can't -+ * possibly have a mapping. -+ */ -+ if ( dfn >> (PTE_PER_TABLE_SHIFT * level) ) -+ return 0; -+ - next_table_mfn = mfn_x(page_to_mfn(table)); - - if ( level == 1 ) -@@ -415,6 +422,9 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - /* Install lower level page table for non-present entries */ - else if ( !iommu_is_pte_present((uint32_t *)pde) ) - { -+ if ( !map ) -+ return 0; -+ - if ( next_table_mfn == 0 ) - { - table = alloc_amd_iommu_pgtable(); -@@ -577,7 +587,7 @@ int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, - } - } - -- if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn) || (pt_mfn[1] == 0) ) -+ if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, true) || (pt_mfn[1] == 0) ) - { - spin_unlock(&hd->arch.mapping_lock); - AMD_IOMMU_DEBUG("Invalid IO pagetable entry dfn = %"PRI_dfn"\n", -@@ -615,24 +625,7 @@ int amd_iommu_unmap_page(struct domain *d, dfn_t dfn, - return 0; - } - -- /* Since HVM domain is initialized with 2 level IO page table, -- * we might need a deeper page table for lager dfn now */ -- if ( is_hvm_domain(d) ) -- { -- int rc = update_paging_mode(d, dfn_x(dfn)); -- -- if ( rc ) -- { -- spin_unlock(&hd->arch.mapping_lock); -- AMD_IOMMU_DEBUG("Update page mode failed dfn = %"PRI_dfn"\n", -- dfn_x(dfn)); -- if ( rc != -EADDRNOTAVAIL ) -- domain_crash(d); -- return rc; -- } -- } -- -- if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn) || (pt_mfn[1] == 0) ) -+ if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, false) ) - { - spin_unlock(&hd->arch.mapping_lock); - AMD_IOMMU_DEBUG("Invalid IO pagetable entry dfn = %"PRI_dfn"\n", -@@ -641,8 +634,11 @@ int amd_iommu_unmap_page(struct domain *d, dfn_t dfn, - return -EFAULT; - } - -- /* mark PTE as 'page not present' */ -- *flush_flags |= clear_iommu_pte_present(pt_mfn[1], dfn_x(dfn)); -+ if ( pt_mfn[1] ) -+ { -+ /* Mark PTE as 'page not present'. */ -+ *flush_flags |= clear_iommu_pte_present(pt_mfn[1], dfn_x(dfn)); -+ } - - spin_unlock(&hd->arch.mapping_lock); - --- -2.11.0 - diff --git a/xen.spec b/xen.spec index ad033cd..8dc8bbc 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.12.1 -Release: 8%{?dist} +Version: 4.12.2 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,43 +114,6 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch Patch54: xen.python38.patch -Patch55: xsa296.patch -Patch56: xsa298.patch -Patch57: xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch -Patch58: xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch -Patch59: xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch -Patch60: xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch -Patch61: xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch -Patch62: xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch -Patch63: xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch -Patch64: xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch -Patch65: xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch -Patch66: xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch -Patch67: xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch -Patch68: xsa301-master-1.patch -Patch69: xsa301-master-2.patch -Patch70: xsa301-master-3.patch -Patch71: xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch -Patch72: xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch -Patch73: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch -Patch74: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch -Patch75: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch76: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch77: xsa304-4.12-1.patch -Patch78: xsa304-4.12-2.patch -Patch79: xsa304-4.12-3.patch -Patch80: xsa305-4.12-1.patch -Patch81: xsa305-4.12-2.patch -Patch82: xsa306-4.12.patch -Patch83: xsa307.patch -Patch84: xsa308.patch -Patch85: xsa309.patch -Patch86: xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch -Patch87: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch -Patch88: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch -Patch89: xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch -Patch90: xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch -Patch91: xsa311-4.12.patch %if %build_qemutrad @@ -349,43 +312,6 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 -%patch77 -p1 -%patch78 -p1 -%patch79 -p1 -%patch80 -p1 -%patch81 -p1 -%patch82 -p1 -%patch83 -p1 -%patch84 -p1 -%patch85 -p1 -%patch86 -p1 -%patch87 -p1 -%patch88 -p1 -%patch89 -p1 -%patch90 -p1 -%patch91 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -949,6 +875,10 @@ fi %endif %changelog +* Mon Jan 06 2020 Michael Young - 4.12.2-1 +- update to 4.12.2 + remove patches for issues now fixed upstream + * Wed Dec 11 2019 Michael Young - 4.12.1-8 - denial of service in find_next_bit() [XSA-307, CVE-2019-19581, CVE-2019-19582] (#1782211) diff --git a/xsa296.patch b/xsa296.patch deleted file mode 100644 index e71ea7f..0000000 --- a/xsa296.patch +++ /dev/null @@ -1,195 +0,0 @@ -From: Andrew Cooper -Subject: xen/hypercall: Don't use BUG() for parameter checking in hypercall_create_continuation() - -Since c/s 1d429034 "hypercall: update vcpu_op to take an unsigned vcpuid", -which incorrectly swapped 'i' for 'u' in the parameter type list, guests have -been able to hit the BUG() in next_args()'s default case. - -Correct these back to 'i'. - -In addition, make adjustments to prevent this class of issue from occurring in -the future - crashing Xen is not an appropriate form of parameter checking. - -Capitalise NEXT_ARG() to catch all uses, to highlight that it is a macro doing -non-function-like things behind the scenes, and undef it when appropriate. -Implement a bad_fmt: block which prints an error, asserts unreachable, and -crashes the guest. - -On the ARM side, drop all parameter checking of p. It is asymmetric with the -x86 side, and akin to expecting memcpy() or sprintf() to check their src/fmt -parameter before use. A caller passing "" or something other than a string -literal will be obvious during code review. - -This is XSA-296. - -Signed-off-by: Andrew Cooper -Acked-by: Julien Grall - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 941bbff4fe..a3da8e9c08 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -383,14 +383,15 @@ void sync_vcpu_execstate(struct vcpu *v) - /* Nothing to do -- no lazy switching */ - } - --#define next_arg(fmt, args) ({ \ -+#define NEXT_ARG(fmt, args) \ -+({ \ - unsigned long __arg; \ - switch ( *(fmt)++ ) \ - { \ - case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ - case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ - case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ -- default: __arg = 0; BUG(); \ -+ default: goto bad_fmt; \ - } \ - __arg; \ - }) -@@ -405,9 +406,6 @@ unsigned long hypercall_create_continuation( - unsigned int i; - va_list args; - -- /* All hypercalls take at least one argument */ -- BUG_ON( !p || *p == '\0' ); -- - current->hcall_preempted = true; - - va_start(args, format); -@@ -415,7 +413,7 @@ unsigned long hypercall_create_continuation( - if ( mcs->flags & MCSF_in_multicall ) - { - for ( i = 0; *p != '\0'; i++ ) -- mcs->call.args[i] = next_arg(p, args); -+ mcs->call.args[i] = NEXT_ARG(p, args); - - /* Return value gets written back to mcs->call.result */ - rc = mcs->call.result; -@@ -431,7 +429,7 @@ unsigned long hypercall_create_continuation( - - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - - switch ( i ) - { -@@ -454,7 +452,7 @@ unsigned long hypercall_create_continuation( - - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - - switch ( i ) - { -@@ -475,8 +473,16 @@ unsigned long hypercall_create_continuation( - va_end(args); - - return rc; -+ -+ bad_fmt: -+ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); -+ ASSERT_UNREACHABLE(); -+ domain_crash(current->domain); -+ return 0; - } - -+#undef NEXT_ARG -+ - void startup_cpu_idle_loop(void) - { - struct vcpu *v = current; -diff --git a/xen/arch/x86/hypercall.c b/xen/arch/x86/hypercall.c -index d483dbaa6b..4643e5eb43 100644 ---- a/xen/arch/x86/hypercall.c -+++ b/xen/arch/x86/hypercall.c -@@ -80,14 +80,15 @@ const hypercall_args_t hypercall_args_table[NR_hypercalls] = - #undef COMP - #undef ARGS - --#define next_arg(fmt, args) ({ \ -+#define NEXT_ARG(fmt, args) \ -+({ \ - unsigned long __arg; \ - switch ( *(fmt)++ ) \ - { \ - case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ - case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ - case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ -- default: __arg = 0; BUG(); \ -+ default: goto bad_fmt; \ - } \ - __arg; \ - }) -@@ -109,7 +110,7 @@ unsigned long hypercall_create_continuation( - if ( mcs->flags & MCSF_in_multicall ) - { - for ( i = 0; *p != '\0'; i++ ) -- mcs->call.args[i] = next_arg(p, args); -+ mcs->call.args[i] = NEXT_ARG(p, args); - } - else - { -@@ -121,7 +122,7 @@ unsigned long hypercall_create_continuation( - { - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - switch ( i ) - { - case 0: regs->rdi = arg; break; -@@ -137,7 +138,7 @@ unsigned long hypercall_create_continuation( - { - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - switch ( i ) - { - case 0: regs->rbx = arg; break; -@@ -154,8 +155,16 @@ unsigned long hypercall_create_continuation( - va_end(args); - - return op; -+ -+ bad_fmt: -+ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); -+ ASSERT_UNREACHABLE(); -+ domain_crash(curr->domain); -+ return 0; - } - -+#undef NEXT_ARG -+ - int hypercall_xlat_continuation(unsigned int *id, unsigned int nr, - unsigned int mask, ...) - { -diff --git a/xen/common/compat/domain.c b/xen/common/compat/domain.c -index 39877b3ab2..2531fa7421 100644 ---- a/xen/common/compat/domain.c -+++ b/xen/common/compat/domain.c -@@ -81,7 +81,7 @@ int compat_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) ar - } - - if ( rc == -ERESTART ) -- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", -+ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", - cmd, vcpuid, arg); - - break; -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 2308588052..65bcd85e34 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1411,7 +1411,7 @@ long do_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg) - - rc = arch_initialise_vcpu(v, arg); - if ( rc == -ERESTART ) -- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", -+ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", - cmd, vcpuid, arg); - - break; diff --git a/xsa298.patch b/xsa298.patch deleted file mode 100644 index aa39042..0000000 --- a/xsa298.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: Jan Beulich -Subject: x86/PV: check GDT/LDT limits during emulation - -Accesses beyond the LDT limit originating from emulation would trigger -the ASSERT() in pv_map_ldt_shadow_page(). On production builds such -accesses would cause an attempt to promote the touched page (offset from -the present LDT base address) to a segment descriptor one. If this -happens to succeed, guest user mode would be able to elevate its -privileges to that of the guest kernel. This is particularly easy when -there's no LDT at all, in which case the LDT base stored internally to -Xen is simply zero. - -Also adjust the ASSERT() that was triggering: It was off by one to -begin with, and for production builds we also better use -ASSERT_UNREACHABLE() instead with suitable recovery code afterwards. - -This is XSA-298. - -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper ---- -v2: Correct 64-bit-only limit check (by folding into the common one). - ---- a/xen/arch/x86/pv/emul-gate-op.c -+++ b/xen/arch/x86/pv/emul-gate-op.c -@@ -51,7 +51,13 @@ static int read_gate_descriptor(unsigned - const seg_desc_t *pdesc = gdt_ldt_desc_ptr(gate_sel); - - if ( (gate_sel < 4) || -- ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) || -+ /* -+ * We're interested in call gates only, which occupy a single -+ * seg_desc_t for 32-bit and a consecutive pair of them for 64-bit. -+ */ -+ ((gate_sel >> 3) + !is_pv_32bit_vcpu(v) >= -+ (gate_sel & 4 ? v->arch.pv.ldt_ents -+ : v->arch.pv.gdt_ents)) || - __get_user(desc, pdesc) ) - return 0; - -@@ -70,7 +76,7 @@ static int read_gate_descriptor(unsigned - if ( !is_pv_32bit_vcpu(v) ) - { - if ( (*ar & 0x1f00) != 0x0c00 || -- (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) || -+ /* Limit check done above already. */ - __get_user(desc, pdesc + 1) || - (desc.b & 0x1f00) ) - return 0; ---- a/xen/arch/x86/pv/emulate.c -+++ b/xen/arch/x86/pv/emulate.c -@@ -31,7 +31,14 @@ int pv_emul_read_descriptor(unsigned int - { - seg_desc_t desc; - -- if ( sel < 4) -+ if ( sel < 4 || -+ /* -+ * Don't apply the GDT limit here, as the selector may be a Xen -+ * provided one. __get_user() will fail (without taking further -+ * action) for ones falling in the gap between guest populated -+ * and Xen ones. -+ */ -+ ((sel & 4) && (sel >> 3) >= v->arch.pv.ldt_ents) ) - desc.b = desc.a = 0; - else if ( __get_user(desc, gdt_ldt_desc_ptr(sel)) ) - return 0; ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -92,12 +92,16 @@ bool pv_map_ldt_shadow_page(unsigned int - BUG_ON(unlikely(in_irq())); - - /* -- * Hardware limit checking should guarantee this property. NB. This is -+ * Prior limit checking should guarantee this property. NB. This is - * safe as updates to the LDT can only be made by MMUEXT_SET_LDT to the - * current vcpu, and vcpu_reset() will block until this vcpu has been - * descheduled before continuing. - */ -- ASSERT((offset >> 3) <= curr->arch.pv.ldt_ents); -+ if ( unlikely((offset >> 3) >= curr->arch.pv.ldt_ents) ) -+ { -+ ASSERT_UNREACHABLE(); -+ return false; -+ } - - if ( is_pv_32bit_domain(currd) ) - linear = (uint32_t)linear; diff --git a/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch b/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch deleted file mode 100644 index fbb9d80..0000000 --- a/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 33d051917d5ef38f678b507a3c832afde48b9b49 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 01/11] x86/mm: L1TF checks don't leave a partial entry - -On detection of a potential L1TF issue, most validation code returns --ERESTART to allow the switch to shadow mode to happen and cause the -original operation to be restarted. - -However, in the validation code, the return value -ERESTART has been -repurposed to indicate 1) the function has partially completed -something which needs to be undone, and 2) calling put_page_type() -should cleanly undo it. This causes problems in several places. - -For L1 tables, on receiving an -ERESTART return from alloc_l1_table(), -alloc_page_type() will set PGT_partial on the page. If for some -reason the original operation never restarts, then on domain -destruction, relinquish_memory() will call free_page_type() on the -page. - -Unfortunately, alloc_ and free_l1_table() aren't set up to deal with -PGT_partial. When returning a failure, alloc_l1_table() always -de-validates whatever it's validated so far, and free_l1_table() -always devalidates the whole page. This means that if -relinquish_memory() calls free_page_type() on an L1 that didn't -complete due to an L1TF, it will call put_page_from_l1e() on "page -entries" that have never been validated. - -For L2+ tables, setting rc to ERESTART causes the rest of the -alloc_lN_table() function to *think* that the entry in question will -have PGT_partial set. This will cause it to set partial_pte = 1. If -relinqush_memory() then calls free_page_type() on one of those pages, -then free_lN_table() will call put_page_from_lNe() on the entry when -it shouldn't. - -Rather than indicating -ERESTART, indicate -EINTR. This is the code -to indicate that nothing has changed from when you started the call -(which is effectively how alloc_l1_table() handles errors). - -mod_lN_entry() shouldn't have any of these types of problems, so leave -potential changes there for a clean-up patch later. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3557cd1178..a1b55c10ff 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1409,7 +1409,7 @@ static int alloc_l1_table(struct page_info *page) - { - if ( !(l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) ) - { -- ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -ERESTART : 0; -+ ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -EINTR : 0; - if ( ret ) - goto out; - } -@@ -1517,7 +1517,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - { - if ( !pv_l1tf_check_l2e(d, l2e) ) - continue; -- rc = -ERESTART; -+ rc = -EINTR; - } - else - rc = get_page_from_l2e(l2e, pfn, d, partial); -@@ -1603,7 +1603,7 @@ static int alloc_l3_table(struct page_info *page) - { - if ( !pv_l1tf_check_l3e(d, l3e) ) - continue; -- rc = -ERESTART; -+ rc = -EINTR; - } - else - rc = get_page_from_l3e(l3e, pfn, d, partial); -@@ -1783,7 +1783,7 @@ static int alloc_l4_table(struct page_info *page) - { - if ( !pv_l1tf_check_l4e(d, l4e) ) - continue; -- rc = -ERESTART; -+ rc = -EINTR; - } - else - rc = get_page_from_l4e(l4e, pfn, d, partial); --- -2.23.0 - diff --git a/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch b/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch deleted file mode 100644 index a74598e..0000000 --- a/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch +++ /dev/null @@ -1,99 +0,0 @@ -From b490792c18f74b76ec8161721c1e07f810e36309 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 02/11] x86/mm: Don't re-set PGT_pinned on a partially - de-validated page - -When unpinning pagetables, if an operation is interrupted, -relinquish_memory() re-sets PGT_pinned so that the un-pin will -pickedup again when the hypercall restarts. - -This is appropriate when put_page_and_type_preemptible() returns --EINTR, which indicates that the page is back in its initial state -(i.e., completely validated). However, for -ERESTART, this leads to a -state where a page has both PGT_pinned and PGT_partial set. - -This happens to work at the moment, although it's not really a -"canonical" state; but in subsequent patches, where we need to make a -distinction in handling between PGT_validated and PGT_partial pages, -this causes issues. - -Move to a "canonical" state by: -- Only re-setting PGT_pinned on -EINTR -- Re-dropping the refcount held by PGT_pinned on -ERESTART - -In the latter case, the PGT_partial bit will be cleared further down -with the rest of the other PGT_partial pages. - -While here, clean up some trainling whitespace. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/domain.c | 31 ++++++++++++++++++++++++++++--- - 1 file changed, 28 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 2585327834..59df8a6d8d 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -114,7 +114,7 @@ static void play_dead(void) - * this case, heap corruption or #PF can occur (when heap debugging is - * enabled). For example, even printk() can involve tasklet scheduling, - * which touches per-cpu vars. -- * -+ * - * Consider very carefully when adding code to *dead_idle. Most hypervisor - * subsystems are unsafe to call. - */ -@@ -1909,9 +1909,34 @@ static int relinquish_memory( - break; - case -ERESTART: - case -EINTR: -+ /* -+ * -EINTR means PGT_validated has been re-set; re-set -+ * PGT_pinned again so that it gets picked up next time -+ * around. -+ * -+ * -ERESTART, OTOH, means PGT_partial is set instead. Put -+ * it back on the list, but don't set PGT_pinned; the -+ * section below will finish off de-validation. But we do -+ * need to drop the general ref associated with -+ * PGT_pinned, since put_page_and_type_preemptible() -+ * didn't do it. -+ * -+ * NB we can do an ASSERT for PGT_validated, since we -+ * "own" the type ref; but theoretically, the PGT_partial -+ * could be cleared by someone else. -+ */ -+ if ( ret == -EINTR ) -+ { -+ ASSERT(page->u.inuse.type_info & PGT_validated); -+ set_bit(_PGT_pinned, &page->u.inuse.type_info); -+ } -+ else -+ put_page(page); -+ - ret = -ERESTART; -+ -+ /* Put the page back on the list and drop the ref we grabbed above */ - page_list_add(page, list); -- set_bit(_PGT_pinned, &page->u.inuse.type_info); - put_page(page); - goto out; - default: -@@ -2161,7 +2186,7 @@ void vcpu_kick(struct vcpu *v) - * pending flag. These values may fluctuate (after all, we hold no - * locks) but the key insight is that each change will cause - * evtchn_upcall_pending to be polled. -- * -+ * - * NB2. We save the running flag across the unblock to avoid a needless - * IPI for domains that we IPI'd to unblock. - */ --- -2.23.0 - diff --git a/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch b/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch deleted file mode 100644 index 226e548..0000000 --- a/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch +++ /dev/null @@ -1,618 +0,0 @@ -From 0f9f61e5737fdd346550ec6e30161fa99e4653fa Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 03/11] x86/mm: Separate out partial_pte tristate into - individual flags - -At the moment, partial_pte is a tri-state that contains two distinct bits -of information: - -1. If zero, the pte at index [nr_validated_ptes] is un-validated. If - non-zero, the pte was last seen with PGT_partial set. - -2. If positive, the pte at index [nr_validated_ptes] does not hold a - general reference count. If negative, it does. - -To make future patches more clear, separate out this functionality -into two distinct, named bits: PTF_partial_set (for #1) and -PTF_partial_general_ref (for #2). - -Additionally, a number of functions which need this information also -take other flags to control behavior (such as `preemptible` and -`defer`). These are hard to read in the caller (since you only see -'true' or 'false'), and ugly when many are added together. In -preparation for adding yet another flag in a future patch, collapse -all of these into a single `flag` variable. - -NB that this does mean checking for what was previously the '-1' -condition a bit more ugly in the put_page_from_lNe functions (since -you have to check for both partial_set and general ref); but this -clause will go away in a future patch. - -Also note that the original comment had an off-by-one error: -partial_flags (like partial_pte before it) concerns -plNe[nr_validated_ptes], not plNe[nr_validated_ptes+1]. - -No functional change intended. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 165 ++++++++++++++++++++++++--------------- - xen/include/asm-x86/mm.h | 41 +++++++--- - 2 files changed, 128 insertions(+), 78 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index a1b55c10ff..3f6f8cc9b8 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1094,20 +1094,35 @@ get_page_from_l1e( - } - - #ifdef CONFIG_PV -+ -+/* -+ * The following flags are used to specify behavior of various get and -+ * put commands. The first two are also stored in page->partial_flags -+ * to indicate the state of the page pointed to by -+ * page->pte[page->nr_validated_entries]. See the comment in mm.h for -+ * more information. -+ */ -+#define PTF_partial_set (1 << 0) -+#define PTF_partial_general_ref (1 << 1) -+#define PTF_preemptible (1 << 2) -+#define PTF_defer (1 << 3) -+ - static int get_page_and_type_from_mfn( - mfn_t mfn, unsigned long type, struct domain *d, -- int partial, int preemptible) -+ unsigned int flags) - { - struct page_info *page = mfn_to_page(mfn); - int rc; -+ bool preemptible = flags & PTF_preemptible, -+ partial_ref = flags & PTF_partial_general_ref; - -- if ( likely(partial >= 0) && -+ if ( likely(!partial_ref) && - unlikely(!get_page_from_mfn(mfn, d)) ) - return -EINVAL; - - rc = _get_page_type(page, type, preemptible); - -- if ( unlikely(rc) && partial >= 0 && -+ if ( unlikely(rc) && !partial_ref && - (!preemptible || page != current->arch.old_guest_table) ) - put_page(page); - -@@ -1117,7 +1132,7 @@ static int get_page_and_type_from_mfn( - define_get_linear_pagetable(l2); - static int - get_page_from_l2e( -- l2_pgentry_t l2e, unsigned long pfn, struct domain *d, int partial) -+ l2_pgentry_t l2e, unsigned long pfn, struct domain *d, unsigned int flags) - { - unsigned long mfn = l2e_get_pfn(l2e); - int rc; -@@ -1129,8 +1144,9 @@ get_page_from_l2e( - return -EINVAL; - } - -- rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, -- partial, false); -+ ASSERT(!(flags & PTF_preemptible)); -+ -+ rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, flags); - if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) ) - rc = 0; - -@@ -1140,7 +1156,7 @@ get_page_from_l2e( - define_get_linear_pagetable(l3); - static int - get_page_from_l3e( -- l3_pgentry_t l3e, unsigned long pfn, struct domain *d, int partial) -+ l3_pgentry_t l3e, unsigned long pfn, struct domain *d, unsigned int flags) - { - int rc; - -@@ -1152,7 +1168,7 @@ get_page_from_l3e( - } - - rc = get_page_and_type_from_mfn( -- l3e_get_mfn(l3e), PGT_l2_page_table, d, partial, 1); -+ l3e_get_mfn(l3e), PGT_l2_page_table, d, flags | PTF_preemptible); - if ( unlikely(rc == -EINVAL) && - !is_pv_32bit_domain(d) && - get_l3_linear_pagetable(l3e, pfn, d) ) -@@ -1164,7 +1180,7 @@ get_page_from_l3e( - define_get_linear_pagetable(l4); - static int - get_page_from_l4e( -- l4_pgentry_t l4e, unsigned long pfn, struct domain *d, int partial) -+ l4_pgentry_t l4e, unsigned long pfn, struct domain *d, unsigned int flags) - { - int rc; - -@@ -1176,7 +1192,7 @@ get_page_from_l4e( - } - - rc = get_page_and_type_from_mfn( -- l4e_get_mfn(l4e), PGT_l3_page_table, d, partial, 1); -+ l4e_get_mfn(l4e), PGT_l3_page_table, d, flags | PTF_preemptible); - if ( unlikely(rc == -EINVAL) && get_l4_linear_pagetable(l4e, pfn, d) ) - rc = 0; - -@@ -1277,7 +1293,7 @@ static void put_data_page(struct page_info *page, bool writeable) - * Note also that this automatically deals correctly with linear p.t.'s. - */ - static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, -- int partial, bool defer) -+ unsigned int flags) - { - int rc = 0; - -@@ -1300,12 +1316,13 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - struct page_info *pg = l2e_get_page(l2e); - struct page_info *ptpg = mfn_to_page(_mfn(pfn)); - -- if ( unlikely(partial > 0) ) -+ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -+ PTF_partial_set ) - { -- ASSERT(!defer); -+ ASSERT(!(flags & PTF_defer)); - rc = _put_page_type(pg, true, ptpg); - } -- else if ( defer ) -+ else if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = ptpg; - current->arch.old_guest_table = pg; -@@ -1322,7 +1339,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - } - - static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, -- int partial, bool defer) -+ unsigned int flags) - { - struct page_info *pg; - int rc; -@@ -1345,13 +1362,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - - pg = l3e_get_page(l3e); - -- if ( unlikely(partial > 0) ) -+ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -+ PTF_partial_set ) - { -- ASSERT(!defer); -+ ASSERT(!(flags & PTF_defer)); - return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); - } - -- if ( defer ) -+ if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -@@ -1366,7 +1384,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - } - - static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, -- int partial, bool defer) -+ unsigned int flags) - { - int rc = 1; - -@@ -1375,13 +1393,14 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - { - struct page_info *pg = l4e_get_page(l4e); - -- if ( unlikely(partial > 0) ) -+ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -+ PTF_partial_set ) - { -- ASSERT(!defer); -+ ASSERT(!(flags & PTF_defer)); - return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); - } - -- if ( defer ) -+ if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -@@ -1492,12 +1511,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; - unsigned int i; -- int rc = 0, partial = page->partial_pte; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags; - - pl2e = map_domain_page(_mfn(pfn)); - - for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; -- i++, partial = 0 ) -+ i++, partial_flags = 0 ) - { - l2_pgentry_t l2e; - -@@ -1520,17 +1540,18 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - rc = -EINTR; - } - else -- rc = get_page_from_l2e(l2e, pfn, d, partial); -+ rc = get_page_from_l2e(l2e, pfn, d, partial_flags); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: 1; -+ /* Set 'set', retain 'general ref' */ -+ page->partial_flags = partial_flags | PTF_partial_set; - } - else if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - else if ( rc < 0 && rc != -EINTR ) -@@ -1539,7 +1560,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1563,7 +1584,8 @@ static int alloc_l3_table(struct page_info *page) - unsigned long pfn = mfn_x(page_to_mfn(page)); - l3_pgentry_t *pl3e; - unsigned int i; -- int rc = 0, partial = page->partial_pte; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags; - - pl3e = map_domain_page(_mfn(pfn)); - -@@ -1578,7 +1600,7 @@ static int alloc_l3_table(struct page_info *page) - memset(pl3e + 4, 0, (L3_PAGETABLE_ENTRIES - 4) * sizeof(*pl3e)); - - for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; -- i++, partial = 0 ) -+ i++, partial_flags = 0 ) - { - l3_pgentry_t l3e = pl3e[i]; - -@@ -1597,7 +1619,8 @@ static int alloc_l3_table(struct page_info *page) - else - rc = get_page_and_type_from_mfn( - l3e_get_mfn(l3e), -- PGT_l2_page_table | PGT_pae_xen_l2, d, partial, 1); -+ PGT_l2_page_table | PGT_pae_xen_l2, d, -+ partial_flags | PTF_preemptible); - } - else if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) - { -@@ -1606,17 +1629,18 @@ static int alloc_l3_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l3e(l3e, pfn, d, partial); -+ rc = get_page_from_l3e(l3e, pfn, d, partial_flags); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: 1; -+ /* Set 'set', leave 'general ref' set if this entry was set */ -+ page->partial_flags = partial_flags | PTF_partial_set; - } - else if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - if ( rc < 0 ) -@@ -1633,7 +1657,7 @@ static int alloc_l3_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1767,10 +1791,11 @@ static int alloc_l4_table(struct page_info *page) - unsigned long pfn = mfn_x(page_to_mfn(page)); - l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); - unsigned int i; -- int rc = 0, partial = page->partial_pte; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags; - - for ( i = page->nr_validated_ptes; i < L4_PAGETABLE_ENTRIES; -- i++, partial = 0 ) -+ i++, partial_flags = 0 ) - { - l4_pgentry_t l4e; - -@@ -1786,12 +1811,13 @@ static int alloc_l4_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l4e(l4e, pfn, d, partial); -+ rc = get_page_from_l4e(l4e, pfn, d, partial_flags); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: 1; -+ /* Set 'set', leave 'general ref' set if this entry was set */ -+ page->partial_flags = partial_flags | PTF_partial_set; - } - else if ( rc < 0 ) - { -@@ -1801,7 +1827,7 @@ static int alloc_l4_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - if ( rc == -EINTR ) - rc = -ERESTART; - else -@@ -1853,19 +1879,20 @@ static int free_l2_table(struct page_info *page) - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; -- int rc = 0, partial = page->partial_pte; -- unsigned int i = page->nr_validated_ptes - !partial; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags, -+ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); - - pl2e = map_domain_page(_mfn(pfn)); - - for ( ; ; ) - { - if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) ) -- rc = put_page_from_l2e(pl2e[i], pfn, partial, false); -+ rc = put_page_from_l2e(pl2e[i], pfn, partial_flags); - if ( rc < 0 ) - break; - -- partial = 0; -+ partial_flags = 0; - - if ( !i-- ) - break; -@@ -1887,12 +1914,14 @@ static int free_l2_table(struct page_info *page) - else if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: -1; -+ page->partial_flags = (partial_flags & PTF_partial_set) ? -+ partial_flags : -+ (PTF_partial_set | PTF_partial_general_ref); - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { - page->nr_validated_ptes = i + 1; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - -@@ -1904,18 +1933,19 @@ static int free_l3_table(struct page_info *page) - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l3_pgentry_t *pl3e; -- int rc = 0, partial = page->partial_pte; -- unsigned int i = page->nr_validated_ptes - !partial; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags, -+ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); - - pl3e = map_domain_page(_mfn(pfn)); - - for ( ; ; ) - { -- rc = put_page_from_l3e(pl3e[i], pfn, partial, 0); -+ rc = put_page_from_l3e(pl3e[i], pfn, partial_flags); - if ( rc < 0 ) - break; - -- partial = 0; -+ partial_flags = 0; - if ( rc == 0 ) - pl3e[i] = unadjust_guest_l3e(pl3e[i], d); - -@@ -1934,12 +1964,14 @@ static int free_l3_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: -1; -+ page->partial_flags = (partial_flags & PTF_partial_set) ? -+ partial_flags : -+ (PTF_partial_set | PTF_partial_general_ref); - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { - page->nr_validated_ptes = i + 1; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - return rc > 0 ? 0 : rc; -@@ -1950,26 +1982,29 @@ static int free_l4_table(struct page_info *page) - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); -- int rc = 0, partial = page->partial_pte; -- unsigned int i = page->nr_validated_ptes - !partial; -+ int rc = 0; -+ unsigned partial_flags = page->partial_flags, -+ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); - - do { - if ( is_guest_l4_slot(d, i) ) -- rc = put_page_from_l4e(pl4e[i], pfn, partial, 0); -+ rc = put_page_from_l4e(pl4e[i], pfn, partial_flags); - if ( rc < 0 ) - break; -- partial = 0; -+ partial_flags = 0; - } while ( i-- ); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: -1; -+ page->partial_flags = (partial_flags & PTF_partial_set) ? -+ partial_flags : -+ (PTF_partial_set | PTF_partial_general_ref); - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { - page->nr_validated_ptes = i + 1; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - -@@ -2247,7 +2282,7 @@ static int mod_l2_entry(l2_pgentry_t *pl2e, - return -EBUSY; - } - -- put_page_from_l2e(ol2e, pfn, 0, true); -+ put_page_from_l2e(ol2e, pfn, PTF_defer); - - return rc; - } -@@ -2315,7 +2350,7 @@ static int mod_l3_entry(l3_pgentry_t *pl3e, - if ( !create_pae_xen_mappings(d, pl3e) ) - BUG(); - -- put_page_from_l3e(ol3e, pfn, 0, 1); -+ put_page_from_l3e(ol3e, pfn, PTF_defer); - return rc; - } - -@@ -2378,7 +2413,7 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, - return -EFAULT; - } - -- put_page_from_l4e(ol4e, pfn, 0, 1); -+ put_page_from_l4e(ol4e, pfn, PTF_defer); - return rc; - } - #endif /* CONFIG_PV */ -@@ -2649,7 +2684,7 @@ int free_page_type(struct page_info *page, unsigned long type, - if ( !(type & PGT_partial) ) - { - page->nr_validated_ptes = 1U << PAGETABLE_ORDER; -- page->partial_pte = 0; -+ page->partial_flags = 0; - } - - switch ( type & PGT_type_mask ) -@@ -2946,7 +2981,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - if ( !(x & PGT_partial) ) - { - page->nr_validated_ptes = 0; -- page->partial_pte = 0; -+ page->partial_flags = 0; - } - page->linear_pt_count = 0; - rc = alloc_page_type(page, type, preemptible); -@@ -3122,7 +3157,7 @@ int new_guest_cr3(mfn_t mfn) - return 0; - } - -- rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, 0, 1); -+ rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, PTF_preemptible); - switch ( rc ) - { - case 0: -@@ -3473,7 +3508,7 @@ long do_mmuext_op( - if ( op.arg1.mfn != 0 ) - { - rc = get_page_and_type_from_mfn( -- _mfn(op.arg1.mfn), PGT_root_page_table, currd, 0, 1); -+ _mfn(op.arg1.mfn), PGT_root_page_table, currd, PTF_preemptible); - - if ( unlikely(rc) ) - { -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 6faa563167..8406ac3c37 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -228,19 +228,34 @@ struct page_info - * setting the flag must not drop that reference, whereas the instance - * clearing it will have to. - * -- * If @partial_pte is positive then PTE at @nr_validated_ptes+1 has -- * been partially validated. This implies that the general reference -- * to the page (acquired from get_page_from_lNe()) would be dropped -- * (again due to the apparent failure) and hence must be re-acquired -- * when resuming the validation, but must not be dropped when picking -- * up the page for invalidation. -+ * If partial_flags & PTF_partial_set is set, then the page at -+ * at @nr_validated_ptes had PGT_partial set as a result of an -+ * operation on the current page. (That page may or may not -+ * still have PGT_partial set.) - * -- * If @partial_pte is negative then PTE at @nr_validated_ptes+1 has -- * been partially invalidated. This is basically the opposite case of -- * above, i.e. the general reference to the page was not dropped in -- * put_page_from_lNe() (due to the apparent failure), and hence it -- * must be dropped when the put operation is resumed (and completes), -- * but it must not be acquired if picking up the page for validation. -+ * If PTF_partial_general_ref is set, then the PTE at -+ * @nr_validated_ptef holds a general reference count for the -+ * page. -+ * -+ * This happens: -+ * - During de-validation, if de-validation of the page was -+ * interrupted -+ * - During validation, if an invalid entry is encountered and -+ * validation is preemptible -+ * - During validation, if PTF_partial_general_ref was set on -+ * this entry to begin with (perhaps because we're picking -+ * up from a partial de-validation). -+ * -+ * When resuming validation, if PTF_partial_general_ref is clear, -+ * then a general reference must be re-acquired; if it is set, no -+ * reference should be acquired. -+ * -+ * When resuming de-validation, if PTF_partial_general_ref is -+ * clear, no reference should be dropped; if it is set, a -+ * reference should be dropped. -+ * -+ * NB that PTF_partial_set and PTF_partial_general_ref are -+ * defined in mm.c, the only place where they are used. - * - * The 3rd field, @linear_pt_count, indicates - * - by a positive value, how many same-level page table entries a page -@@ -251,7 +266,7 @@ struct page_info - struct { - u16 nr_validated_ptes:PAGETABLE_ORDER + 1; - u16 :16 - PAGETABLE_ORDER - 1 - 2; -- s16 partial_pte:2; -+ u16 partial_flags:2; - s16 linear_pt_count; - }; - --- -2.23.0 - diff --git a/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch b/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch deleted file mode 100644 index d07c233..0000000 --- a/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch +++ /dev/null @@ -1,140 +0,0 @@ -From db1d801aa8dcb918a27486a6e8d9cf5d7307dec3 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 04/11] x86/mm: Use flags for _put_page_type rather than a - boolean - -This is in mainly in preparation for _put_page_type taking the -partial_flags value in the future. It also makes it easier to read in -the caller (since you see a flag name rather than `true` or `false`). - -No functional change intended. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 25 +++++++++++++------------ - 1 file changed, 13 insertions(+), 12 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3f6f8cc9b8..0740b61af8 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1200,7 +1200,7 @@ get_page_from_l4e( - } - #endif /* CONFIG_PV */ - --static int _put_page_type(struct page_info *page, bool preemptible, -+static int _put_page_type(struct page_info *page, unsigned int flags, - struct page_info *ptpg); - - void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner) -@@ -1320,7 +1320,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - PTF_partial_set ) - { - ASSERT(!(flags & PTF_defer)); -- rc = _put_page_type(pg, true, ptpg); -+ rc = _put_page_type(pg, PTF_preemptible, ptpg); - } - else if ( flags & PTF_defer ) - { -@@ -1329,7 +1329,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - } - else - { -- rc = _put_page_type(pg, true, ptpg); -+ rc = _put_page_type(pg, PTF_preemptible, ptpg); - if ( likely(!rc) ) - put_page(pg); - } -@@ -1366,7 +1366,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - PTF_partial_set ) - { - ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - } - - if ( flags & PTF_defer ) -@@ -1376,7 +1376,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - return 0; - } - -- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - -@@ -1397,7 +1397,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - PTF_partial_set ) - { - ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - } - - if ( flags & PTF_defer ) -@@ -1407,7 +1407,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - return 0; - } - -- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - } -@@ -2757,10 +2757,11 @@ static int _put_final_page_type(struct page_info *page, unsigned long type, - } - - --static int _put_page_type(struct page_info *page, bool preemptible, -+static int _put_page_type(struct page_info *page, unsigned int flags, - struct page_info *ptpg) - { - unsigned long nx, x, y = page->u.inuse.type_info; -+ bool preemptible = flags & PTF_preemptible; - - ASSERT(current_locked_page_ne_check(page)); - -@@ -2969,7 +2970,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - - if ( unlikely(iommu_ret) ) - { -- _put_page_type(page, false, NULL); -+ _put_page_type(page, 0, NULL); - rc = iommu_ret; - goto out; - } -@@ -2996,7 +2997,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - - void put_page_type(struct page_info *page) - { -- int rc = _put_page_type(page, false, NULL); -+ int rc = _put_page_type(page, 0, NULL); - ASSERT(rc == 0); - (void)rc; - } -@@ -3013,7 +3014,7 @@ int get_page_type(struct page_info *page, unsigned long type) - - int put_page_type_preemptible(struct page_info *page) - { -- return _put_page_type(page, true, NULL); -+ return _put_page_type(page, PTF_preemptible, NULL); - } - - int get_page_type_preemptible(struct page_info *page, unsigned long type) -@@ -3030,7 +3031,7 @@ int put_old_guest_table(struct vcpu *v) - if ( !v->arch.old_guest_table ) - return 0; - -- switch ( rc = _put_page_type(v->arch.old_guest_table, true, -+ switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, - v->arch.old_guest_ptpg) ) - { - case -EINTR: --- -2.23.0 - diff --git a/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch b/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch deleted file mode 100644 index 9cfbb73..0000000 --- a/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 6f257854c8778774210281c5c21028c4b7739b44 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 05/11] x86/mm: Rework get_page_and_type_from_mfn conditional - -Make it easier to read by declaring the conditions in which we will -retain the ref, rather than the conditions under which we release it. - -The only way (page == current->arch.old_guest_table) can be true is if -preemptible is true; so remove this from the query itself, and add an -ASSERT() to that effect on the opposite path. - -No functional change intended. - -NB that alloc_lN_table() mishandle the "linear pt failure" situation -described in the comment; this will be addressed in a future patch. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 39 +++++++++++++++++++++++++++++++++++++-- - 1 file changed, 37 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 0740b61af8..0a4d39a2c3 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1122,8 +1122,43 @@ static int get_page_and_type_from_mfn( - - rc = _get_page_type(page, type, preemptible); - -- if ( unlikely(rc) && !partial_ref && -- (!preemptible || page != current->arch.old_guest_table) ) -+ /* -+ * Retain the refcount if: -+ * - page is fully validated (rc == 0) -+ * - page is not validated (rc < 0) but: -+ * - We came in with a reference (partial_ref) -+ * - page is partially validated but there's been an error -+ * (page == current->arch.old_guest_table) -+ * -+ * The partial_ref-on-error clause is worth an explanation. There -+ * are two scenarios where partial_ref might be true coming in: -+ * - mfn has been partially demoted as type `type`; i.e. has -+ * PGT_partial set -+ * - mfn has been partially demoted as L(type+1) (i.e., a linear -+ * page; e.g. we're being called from get_page_from_l2e with -+ * type == PGT_l1_table, but the mfn is PGT_l2_table) -+ * -+ * If there's an error, in the first case, _get_page_type will -+ * either return -ERESTART, in which case we want to retain the -+ * ref (as the caller will consider it retained), or -EINVAL, in -+ * which case old_guest_table will be set; in both cases, we need -+ * to retain the ref. -+ * -+ * In the second case, if there's an error, _get_page_type() can -+ * *only* return -EINVAL, and *never* set old_guest_table. In -+ * that case we also want to retain the reference, to allow the -+ * page to continue to be torn down (i.e., PGT_partial cleared) -+ * safely. -+ * -+ * Also note that we shouldn't be able to leave with the reference -+ * count retained unless we succeeded, or the operation was -+ * preemptible. -+ */ -+ if ( likely(!rc) || partial_ref ) -+ /* nothing */; -+ else if ( page == current->arch.old_guest_table ) -+ ASSERT(preemptible); -+ else - put_page(page); - - return rc; --- -2.23.0 - diff --git a/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch b/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch deleted file mode 100644 index 72ee3ea..0000000 --- a/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 4ad70553611a7a4e4494d5a3b51b5cc295a488e0 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 06/11] x86/mm: Have alloc_l[23]_table clear partial_flags when - preempting - -In order to allow recursive pagetable promotions and demotions to be -interrupted, Xen must keep track of the state of the sub-pages -promoted or demoted. This is stored in two elements in the page -struct: nr_entries_validated and partial_flags. - -The rule is that entries [0, nr_entries_validated) should always be -validated and hold a general reference count. If partial_flags is -zero, then [nr_entries_validated] is not validated and no reference -count is held. If PTF_partial_set is set, then [nr_entries_validated] -is partially validated. - -At the moment, a distinction is made between promotion and demotion -with regard to whether the entry itself "holds" a general reference -count: when entry promotion is interrupted (i.e., returns -ERESTART), -the entry is not considered to hold a reference; when entry demotion -is interrupted, the entry is still considered to hold a general -reference. - -PTF_partial_general_ref is used to distinguish between these cases. -If clear, it's a partial promotion => no general reference count held -by the entry; if set, it's partial demotion, so a general reference -count held. Because promotions and demotions can be interleaved, this -value is passed to get_page_and_type_from_mfn and put_page_from_l*e, -to be able to properly handle reference counts. - -Unfortunately, when alloc_l[23]_table check hypercall_preempt_check() -and return -ERESTART, they set nr_entries_validated, but don't clear -partial_flags. - -If we were picking up from a previously-interrupted promotion, that -means that PTF_partial_set would be set even though -[nr_entries_validated] was not partially validated. This means that -if the page in this state were de-validated, put_page_type() would -erroneously be called on that entry. - -Perhaps worse, if we were racing with a de-validation, then we might -leave both PTF_partial_set and PTF_partial_general_ref; and when -de-validation picked up again, both the type and the general ref would -be erroneously dropped from [nr_entries_validated]. - -In a sense, the real issue here is code duplication. Rather than -duplicate the interruption code, set rc to -EINTR and fall through to -the code which already handles that case correctly. - -Given the logic at this point, it should be impossible for -partial_flags to be non-zero; add an ASSERT() to catch any changes. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 25 ++++++------------------- - 1 file changed, 6 insertions(+), 19 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 0a4d39a2c3..bbd29a68f4 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1554,21 +1554,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; - i++, partial_flags = 0 ) - { -- l2_pgentry_t l2e; -+ l2_pgentry_t l2e = pl2e[i]; - - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) -- { -- page->nr_validated_ptes = i; -- rc = -ERESTART; -- break; -- } -- -- if ( !is_guest_l2_slot(d, type, i) ) -+ rc = -EINTR; -+ else if ( !is_guest_l2_slot(d, type, i) ) - continue; -- -- l2e = pl2e[i]; -- -- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) -+ else if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) - { - if ( !pv_l1tf_check_l2e(d, l2e) ) - continue; -@@ -1640,13 +1632,8 @@ static int alloc_l3_table(struct page_info *page) - l3_pgentry_t l3e = pl3e[i]; - - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) -- { -- page->nr_validated_ptes = i; -- rc = -ERESTART; -- break; -- } -- -- if ( is_pv_32bit_domain(d) && (i == 3) ) -+ rc = -EINTR; -+ else if ( is_pv_32bit_domain(d) && (i == 3) ) - { - if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || - (l3e_get_flags(l3e) & l3_disallow_mask(d)) ) --- -2.23.0 - diff --git a/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch b/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch deleted file mode 100644 index ef390e2..0000000 --- a/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch +++ /dev/null @@ -1,378 +0,0 @@ -From 51fe4e67d954649fcf103116be6206a769f0db1e Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 07/11] x86/mm: Always retain a general ref on partial - -In order to allow recursive pagetable promotions and demotions to be -interrupted, Xen must keep track of the state of the sub-pages -promoted or demoted. This is stored in two elements in the page struct: -nr_entries_validated and partial_flags. - -The rule is that entries [0, nr_entries_validated) should always be -validated and hold a general reference count. If partial_flags is -zero, then [nr_entries_validated] is not validated and no reference -count is held. If PTF_partial_set is set, then [nr_entries_validated] -is partially validated. - -At the moment, a distinction is made between promotion and demotion -with regard to whether the entry itself "holds" a general reference -count: when entry promotion is interrupted (i.e., returns -ERESTART), -the entry is not considered to hold a reference; when entry demotion -is interrupted, the entry is still considered to hold a general -reference. - -PTF_partial_general_ref is used to distinguish between these cases. -If clear, it's a partial promotion => no general reference count held -by the entry; if set, it's partial demotion, so a general reference -count held. Because promotions and demotions can be interleaved, this -value is passed to get_page_and_type_from_mfn and put_page_from_l*e, -to be able to properly handle reference counts. - -Unfortunately, because a refcount is not held, it is possible to -engineer a situation where PFT_partial_set is set but the page in -question has been assigned to another domain. A sketch is provided in -the appendix. - -Fix this by having the parent page table entry hold a general -reference count whenever PFT_partial_set is set. (For clarity of -change, keep two separate flags. These will be collapsed in a -subsequent changeset.) - -This has two basic implications. On the put_page_from_lNe() side, -this mean that the (partial_set && !partial_ref) case can never happen, -and no longer needs to be special-cased. - -Secondly, because both flags are set together, there's no need to carry over -existing bits from partial_pte. - -(NB there is still another issue with calling _put_page_type() on a -page which had PGT_partial set; that will be handled in a subsequent -patch.) - -On the get_page_and_type_from_mfn() side, we need to distinguish -between callers which hold a reference on partial (i.e., -alloc_lN_table()), and those which do not (new_cr3, PIN_LN_TABLE, and -so on): pass a flag if the type should be retained on interruption. - -NB that since l1 promotion can't be preempted, that get_page_from_l2e -can't return -ERESTART. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ------ -* Appendix: Engineering PTF_partial_set while a page belongs to a - foreign domain - -Suppose A is a page which can be promoted to an l3, and B is a page -which can be promoted to an l2, and A[x] points to B. B has -PGC_allocated set but no other general references. - -V1: PIN_L3 A. - A is validated, B is validated. - A.type_count = 1 | PGT_validated | PGT_pinned - B.type_count = 1 | PGT_validated - B.count = 2 | PGC_allocated (A[x] holds a general ref) - -V1: UNPIN A. - A begins de-validation. - Arrange to be interrupted when i < x - V1->old_guest_table = A - V1->old_guest_table_ref_held = false - A.type_count = 1 | PGT_partial - A.nr_validated_entries = i < x - B.type_count = 0 - B.count = 1 | PGC_allocated - -V2: MOD_L4_ENTRY to point some l4e to A. - Picks up re-validation of A. - Arrange to be interrupted halfway through B's validation - B.type_count = 1 | PGT_partial - B.count = 2 | PGC_allocated (PGT_partial holds a general ref) - A.type_count = 1 | PGT_partial - A.nr_validated_entries = x - A.partial_pte = PTF_partial_set - -V3: MOD_L3_ENTRY to point some other l3e (not in A) to B. - Validates B. - B.type_count = 1 | PGT_validated - B.count = 2 | PGC_allocated ("other l3e" holds a general ref) - -V3: MOD_L3_ENTRY to clear l3e pointing to B. - Devalidates B. - B.type_count = 0 - B.count = 1 | PGC_allocated - -V3: decrease_reservation(B) - Clears PGC_allocated - B.count = 0 => B is freed - -B gets assigned to a different domain - -V1: Restarts UNPIN of A - put_old_guest_table(A) - ... - free_l3_table(A) - -Now since A.partial_flags has PTF_partial_set, free_l3_table() will -call put_page_from_l3e() on A[x], which points to B, while B is owned -by another domain. - -If A[x] held a general refcount for B on partial validation, as it does -for partial de-validation, then B would still have a reference count of -1 after PGC_allocated was freed; so B wouldn't be freed until after -put_page_from_l3e() had happend on A[x]. ---- - xen/arch/x86/mm.c | 84 +++++++++++++++++++++++----------------- - xen/include/asm-x86/mm.h | 15 ++++--- - 2 files changed, 58 insertions(+), 41 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index bbd29a68f4..4d3ebf341d 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1102,10 +1102,11 @@ get_page_from_l1e( - * page->pte[page->nr_validated_entries]. See the comment in mm.h for - * more information. - */ --#define PTF_partial_set (1 << 0) --#define PTF_partial_general_ref (1 << 1) --#define PTF_preemptible (1 << 2) --#define PTF_defer (1 << 3) -+#define PTF_partial_set (1 << 0) -+#define PTF_partial_general_ref (1 << 1) -+#define PTF_preemptible (1 << 2) -+#define PTF_defer (1 << 3) -+#define PTF_retain_ref_on_restart (1 << 4) - - static int get_page_and_type_from_mfn( - mfn_t mfn, unsigned long type, struct domain *d, -@@ -1114,7 +1115,11 @@ static int get_page_and_type_from_mfn( - struct page_info *page = mfn_to_page(mfn); - int rc; - bool preemptible = flags & PTF_preemptible, -- partial_ref = flags & PTF_partial_general_ref; -+ partial_ref = flags & PTF_partial_general_ref, -+ partial_set = flags & PTF_partial_set, -+ retain_ref = flags & PTF_retain_ref_on_restart; -+ -+ ASSERT(partial_ref == partial_set); - - if ( likely(!partial_ref) && - unlikely(!get_page_from_mfn(mfn, d)) ) -@@ -1127,13 +1132,15 @@ static int get_page_and_type_from_mfn( - * - page is fully validated (rc == 0) - * - page is not validated (rc < 0) but: - * - We came in with a reference (partial_ref) -+ * - page is partially validated (rc == -ERESTART), and the -+ * caller has asked the ref to be retained in that case - * - page is partially validated but there's been an error - * (page == current->arch.old_guest_table) - * - * The partial_ref-on-error clause is worth an explanation. There - * are two scenarios where partial_ref might be true coming in: -- * - mfn has been partially demoted as type `type`; i.e. has -- * PGT_partial set -+ * - mfn has been partially promoted / demoted as type `type`; -+ * i.e. has PGT_partial set - * - mfn has been partially demoted as L(type+1) (i.e., a linear - * page; e.g. we're being called from get_page_from_l2e with - * type == PGT_l1_table, but the mfn is PGT_l2_table) -@@ -1156,7 +1163,8 @@ static int get_page_and_type_from_mfn( - */ - if ( likely(!rc) || partial_ref ) - /* nothing */; -- else if ( page == current->arch.old_guest_table ) -+ else if ( page == current->arch.old_guest_table || -+ (retain_ref && rc == -ERESTART) ) - ASSERT(preemptible); - else - put_page(page); -@@ -1354,8 +1362,8 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == - PTF_partial_set ) - { -- ASSERT(!(flags & PTF_defer)); -- rc = _put_page_type(pg, PTF_preemptible, ptpg); -+ /* partial_set should always imply partial_ref */ -+ BUG(); - } - else if ( flags & PTF_defer ) - { -@@ -1400,8 +1408,8 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == - PTF_partial_set ) - { -- ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ /* partial_set should always imply partial_ref */ -+ BUG(); - } - - if ( flags & PTF_defer ) -@@ -1431,8 +1439,8 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == - PTF_partial_set ) - { -- ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ /* partial_set should always imply partial_ref */ -+ BUG(); - } - - if ( flags & PTF_defer ) -@@ -1569,13 +1577,22 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - else - rc = get_page_from_l2e(l2e, pfn, d, partial_flags); - -- if ( rc == -ERESTART ) -- { -- page->nr_validated_ptes = i; -- /* Set 'set', retain 'general ref' */ -- page->partial_flags = partial_flags | PTF_partial_set; -- } -- else if ( rc == -EINTR && i ) -+ /* -+ * It shouldn't be possible for get_page_from_l2e to return -+ * -ERESTART, since we never call this with PTF_preemptible. -+ * (alloc_l1_table may return -EINTR on an L1TF-vulnerable -+ * entry.) -+ * -+ * NB that while on a "clean" promotion, we can never get -+ * PGT_partial. It is possible to arrange for an l2e to -+ * contain a partially-devalidated l2; but in that case, both -+ * of the following functions will fail anyway (the first -+ * because the page in question is not an l1; the second -+ * because the page is not fully validated). -+ */ -+ ASSERT(rc != -ERESTART); -+ -+ if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; - page->partial_flags = 0; -@@ -1584,6 +1601,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - else if ( rc < 0 && rc != -EINTR ) - { - gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i); -+ ASSERT(current->arch.old_guest_table == NULL); - if ( i ) - { - page->nr_validated_ptes = i; -@@ -1642,7 +1660,7 @@ static int alloc_l3_table(struct page_info *page) - rc = get_page_and_type_from_mfn( - l3e_get_mfn(l3e), - PGT_l2_page_table | PGT_pae_xen_l2, d, -- partial_flags | PTF_preemptible); -+ partial_flags | PTF_preemptible | PTF_retain_ref_on_restart); - } - else if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) - { -@@ -1651,13 +1669,14 @@ static int alloc_l3_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l3e(l3e, pfn, d, partial_flags); -+ rc = get_page_from_l3e(l3e, pfn, d, -+ partial_flags | PTF_retain_ref_on_restart); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = partial_flags | PTF_partial_set; -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i ) - { -@@ -1833,13 +1852,14 @@ static int alloc_l4_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l4e(l4e, pfn, d, partial_flags); -+ rc = get_page_from_l4e(l4e, pfn, d, -+ partial_flags | PTF_retain_ref_on_restart); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = partial_flags | PTF_partial_set; -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc < 0 ) - { -@@ -1936,9 +1956,7 @@ static int free_l2_table(struct page_info *page) - else if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = (partial_flags & PTF_partial_set) ? -- partial_flags : -- (PTF_partial_set | PTF_partial_general_ref); -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { -@@ -1986,9 +2004,7 @@ static int free_l3_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = (partial_flags & PTF_partial_set) ? -- partial_flags : -- (PTF_partial_set | PTF_partial_general_ref); -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { -@@ -2019,9 +2035,7 @@ static int free_l4_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = (partial_flags & PTF_partial_set) ? -- partial_flags : -- (PTF_partial_set | PTF_partial_general_ref); -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 8406ac3c37..02079e1324 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -238,22 +238,25 @@ struct page_info - * page. - * - * This happens: -- * - During de-validation, if de-validation of the page was -+ * - During validation or de-validation, if the operation was - * interrupted - * - During validation, if an invalid entry is encountered and - * validation is preemptible - * - During validation, if PTF_partial_general_ref was set on -- * this entry to begin with (perhaps because we're picking -- * up from a partial de-validation). -+ * this entry to begin with (perhaps because it picked up a -+ * previous operation) - * -- * When resuming validation, if PTF_partial_general_ref is clear, -- * then a general reference must be re-acquired; if it is set, no -- * reference should be acquired. -+ * When resuming validation, if PTF_partial_general_ref is -+ * clear, then a general reference must be re-acquired; if it -+ * is set, no reference should be acquired. - * - * When resuming de-validation, if PTF_partial_general_ref is - * clear, no reference should be dropped; if it is set, a - * reference should be dropped. - * -+ * NB at the moment, PTF_partial_set should be set if and only if -+ * PTF_partial_general_ref is set. -+ * - * NB that PTF_partial_set and PTF_partial_general_ref are - * defined in mm.c, the only place where they are used. - * --- -2.23.0 - diff --git a/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch b/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch deleted file mode 100644 index 6cf41d1..0000000 --- a/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch +++ /dev/null @@ -1,227 +0,0 @@ -From 8a8d836f7f7418e659d37817a66cd7a6b115042b Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 08/11] x86/mm: Collapse PTF_partial_set and - PTF_partial_general_ref into one - -...now that they are equivalent. No functional change intended. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 50 +++++++++++----------------------------- - xen/include/asm-x86/mm.h | 29 +++++++++++------------ - 2 files changed, 26 insertions(+), 53 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 4d3ebf341d..886e93b8aa 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1097,13 +1097,12 @@ get_page_from_l1e( - - /* - * The following flags are used to specify behavior of various get and -- * put commands. The first two are also stored in page->partial_flags -- * to indicate the state of the page pointed to by -+ * put commands. The first is also stored in page->partial_flags to -+ * indicate the state of the page pointed to by - * page->pte[page->nr_validated_entries]. See the comment in mm.h for - * more information. - */ - #define PTF_partial_set (1 << 0) --#define PTF_partial_general_ref (1 << 1) - #define PTF_preemptible (1 << 2) - #define PTF_defer (1 << 3) - #define PTF_retain_ref_on_restart (1 << 4) -@@ -1115,13 +1114,10 @@ static int get_page_and_type_from_mfn( - struct page_info *page = mfn_to_page(mfn); - int rc; - bool preemptible = flags & PTF_preemptible, -- partial_ref = flags & PTF_partial_general_ref, - partial_set = flags & PTF_partial_set, - retain_ref = flags & PTF_retain_ref_on_restart; - -- ASSERT(partial_ref == partial_set); -- -- if ( likely(!partial_ref) && -+ if ( likely(!partial_set) && - unlikely(!get_page_from_mfn(mfn, d)) ) - return -EINVAL; - -@@ -1131,14 +1127,14 @@ static int get_page_and_type_from_mfn( - * Retain the refcount if: - * - page is fully validated (rc == 0) - * - page is not validated (rc < 0) but: -- * - We came in with a reference (partial_ref) -+ * - We came in with a reference (partial_set) - * - page is partially validated (rc == -ERESTART), and the - * caller has asked the ref to be retained in that case - * - page is partially validated but there's been an error - * (page == current->arch.old_guest_table) - * -- * The partial_ref-on-error clause is worth an explanation. There -- * are two scenarios where partial_ref might be true coming in: -+ * The partial_set-on-error clause is worth an explanation. There -+ * are two scenarios where partial_set might be true coming in: - * - mfn has been partially promoted / demoted as type `type`; - * i.e. has PGT_partial set - * - mfn has been partially demoted as L(type+1) (i.e., a linear -@@ -1161,7 +1157,7 @@ static int get_page_and_type_from_mfn( - * count retained unless we succeeded, or the operation was - * preemptible. - */ -- if ( likely(!rc) || partial_ref ) -+ if ( likely(!rc) || partial_set ) - /* nothing */; - else if ( page == current->arch.old_guest_table || - (retain_ref && rc == -ERESTART) ) -@@ -1359,13 +1355,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - struct page_info *pg = l2e_get_page(l2e); - struct page_info *ptpg = mfn_to_page(_mfn(pfn)); - -- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -- PTF_partial_set ) -- { -- /* partial_set should always imply partial_ref */ -- BUG(); -- } -- else if ( flags & PTF_defer ) -+ if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = ptpg; - current->arch.old_guest_table = pg; -@@ -1405,13 +1395,6 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - - pg = l3e_get_page(l3e); - -- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -- PTF_partial_set ) -- { -- /* partial_set should always imply partial_ref */ -- BUG(); -- } -- - if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); -@@ -1436,13 +1419,6 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - { - struct page_info *pg = l4e_get_page(l4e); - -- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -- PTF_partial_set ) -- { -- /* partial_set should always imply partial_ref */ -- BUG(); -- } -- - if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); -@@ -1676,7 +1652,7 @@ static int alloc_l3_table(struct page_info *page) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i ) - { -@@ -1859,7 +1835,7 @@ static int alloc_l4_table(struct page_info *page) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc < 0 ) - { -@@ -1956,7 +1932,7 @@ static int free_l2_table(struct page_info *page) - else if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { -@@ -2004,7 +1980,7 @@ static int free_l3_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { -@@ -2035,7 +2011,7 @@ static int free_l4_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 02079e1324..f0fd35bf6b 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -233,7 +233,7 @@ struct page_info - * operation on the current page. (That page may or may not - * still have PGT_partial set.) - * -- * If PTF_partial_general_ref is set, then the PTE at -+ * Additionally, if PTF_partial_set is set, then the PTE at - * @nr_validated_ptef holds a general reference count for the - * page. - * -@@ -242,23 +242,20 @@ struct page_info - * interrupted - * - During validation, if an invalid entry is encountered and - * validation is preemptible -- * - During validation, if PTF_partial_general_ref was set on -- * this entry to begin with (perhaps because it picked up a -+ * - During validation, if PTF_partial_set was set on this -+ * entry to begin with (perhaps because it picked up a - * previous operation) - * -- * When resuming validation, if PTF_partial_general_ref is -- * clear, then a general reference must be re-acquired; if it -- * is set, no reference should be acquired. -+ * When resuming validation, if PTF_partial_set is clear, then -+ * a general reference must be re-acquired; if it is set, no -+ * reference should be acquired. - * -- * When resuming de-validation, if PTF_partial_general_ref is -- * clear, no reference should be dropped; if it is set, a -- * reference should be dropped. -+ * When resuming de-validation, if PTF_partial_set is clear, -+ * no reference should be dropped; if it is set, a reference -+ * should be dropped. - * -- * NB at the moment, PTF_partial_set should be set if and only if -- * PTF_partial_general_ref is set. -- * -- * NB that PTF_partial_set and PTF_partial_general_ref are -- * defined in mm.c, the only place where they are used. -+ * NB that PTF_partial_set is defined in mm.c, the only place -+ * where it is used. - * - * The 3rd field, @linear_pt_count, indicates - * - by a positive value, how many same-level page table entries a page -@@ -268,8 +265,8 @@ struct page_info - */ - struct { - u16 nr_validated_ptes:PAGETABLE_ORDER + 1; -- u16 :16 - PAGETABLE_ORDER - 1 - 2; -- u16 partial_flags:2; -+ u16 :16 - PAGETABLE_ORDER - 1 - 1; -+ u16 partial_flags:1; - s16 linear_pt_count; - }; - --- -2.23.0 - diff --git a/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch b/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch deleted file mode 100644 index bbaba79..0000000 --- a/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch +++ /dev/null @@ -1,106 +0,0 @@ -From da3d1d258e54fe600f7f75287183b74d957ec63b Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 09/11] x86/mm: Properly handle linear pagetable promotion - failures - -In order to allow recursive pagetable promotions and demotions to be -interrupted, Xen must keep track of the state of the sub-pages -promoted or demoted. This is stored in two elements in the page -struct: nr_entries_validated and partial_flags. - -The rule is that entries [0, nr_entries_validated) should always be -validated and hold a general reference count. If partial_flags is -zero, then [nr_entries_validated] is not validated and no reference -count is held. If PTF_partial_set is set, then [nr_entries_validated] -is partially validated, and a general reference count is held. - -Unfortunately, in cases where an entry began with PTF_partial_set set, -and get_page_from_lNe() returns -EINVAL, the PTF_partial_set bit is -erroneously dropped. (This scenario can be engineered mainly by the -use of interleaving of promoting and demoting a page which has "linear -pagetable" entries; see the appendix for a sketch.) This means that -we will "leak" a general reference count on the page in question, -preventing the page from being freed. - -Fix this by setting page->partial_flags to the partial_flags local -variable. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ------ -Appendix - -Suppose A and B can both be promoted to L2 pages, and A[x] points to B. - -V1: PIN_L2 B. - B.type_count = 1 | PGT_validated - B.count = 2 | PGC_allocated - -V1: MOD_L3_ENTRY pointing something to A. - In the process of validating A[x], grab an extra type / ref on B: - B.type_count = 2 | PGT_validated - B.count = 3 | PGC_allocated - A.type_count = 1 | PGT_validated - A.count = 2 | PGC_allocated - -V1: UNPIN B. - B.type_count = 1 | PGT_validate - B.count = 2 | PGC_allocated - -V1: MOD_L3_ENTRY removing the reference to A. - De-validate A, down to A[x], which points to B. - Drop the final type on B. Arrange to be interrupted. - B.type_count = 1 | PGT_partial - B.count = 2 | PGC_allocated - A.type_count = 1 | PGT_partial - A.nr_validated_entries = x - A.partial_pte = -1 - -V2: MOD_L3_ENTRY adds a reference to A. - -At this point, get_page_from_l2e(A[x]) tries -get_page_and_type_from_mfn(), which fails because it's the wrong type; -and get_l2_linear_pagetable() also fails, because B isn't validated as -an l2 anymore. ---- - xen/arch/x86/mm.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 886e93b8aa..0a094291da 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1581,7 +1581,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1674,7 +1674,7 @@ static int alloc_l3_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1845,7 +1845,7 @@ static int alloc_l4_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - if ( rc == -EINTR ) - rc = -ERESTART; - else --- -2.23.0 - diff --git a/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch b/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch deleted file mode 100644 index 7d5f022..0000000 --- a/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch +++ /dev/null @@ -1,166 +0,0 @@ -From b3e169dc8daeae85b0b51c25fdb142e2e552ec7f Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 10/11] x86/mm: Fix nested de-validation on error - -If an invalid entry is discovered when validating a page-table tree, -the entire tree which has so far been validated must be de-validated. -Since this may take a long time, alloc_l[2-4]_table() set current -vcpu's old_guest_table immediately; put_old_guest_table() will make -sure that put_page_type() will be called to finish off the -de-validation before any other MMU operations can happen on the vcpu. - -The invariant for partial pages should be: - -* Entries [0, nr_validated_ptes) should be completely validated; - put_page_type() will de-validate these. - -* If [nr_validated_ptes] is partially validated, partial_flags should - set PTF_partiaL_set. put_page_type() will be called on this page to - finish off devalidation, and the appropriate refcount adjustments - will be done. - -alloc_l[2-3]_table() indicates partial validation to its callers by -setting current->old_guest_table. - -Unfortunately, this is mishandled. - -Take the case where validating lNe[x] returns an error. - -First, alloc_l3_table() doesn't check old_guest_table at all; as a -result, partial_flags is not set when it should be. nr_validated_ptes -is set to x; and since PFT_partial_set clear, de-validation resumes at -nr_validated_ptes-1. This means that the l2 page at pl3e[x] will not -have put_page_type() called on it when de-validating the rest of the -l3: it will be stuck in the PGT_partial state until the domain is -destroyed, or until it is re-used as an l2. (Any other page type will -fail.) - -Worse, alloc_l4_table(), rather than setting PTF_partial_set as it -should, sets nr_validated_ptes to x+1. When de-validating, since -partial is 0, this will correctly resume calling put_page_type at [x]; -but, if the put_page_type() is never called, but instead -get_page_type() is called, validation will pick up at [x+1], -neglecting to validate [x]. If the rest of the validation succeeds, -the l4 will be validated even though [x] is invalid. - -Fix this in both cases by setting PTF_partial_set if old_guest_table -is set. - -While here, add some safety catches: -- old_guest_table must point to the page contained in - [nr_validated_ptes]. -- alloc_l1_page shouldn't set old_guest_table - -If we experience one of these situations in production builds, it's -safer to avoid calling put_page_type for the pages in question. If -they have PGT_partial set, they will be cleaned up on domain -destruction; if not, we have no idea whether a type count is safe to -drop. Retaining an extra type ref that should have been dropped may -trigger a BUG() on the free_domain_page() path, but dropping a type -count that shouldn't be dropped may cause a privilege escalation. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 51 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 0a094291da..a432e69c74 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1580,6 +1580,20 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - ASSERT(current->arch.old_guest_table == NULL); - if ( i ) - { -+ /* -+ * alloc_l1_table() doesn't set old_guest_table; it does -+ * its own tear-down immediately on failure. If it -+ * did we'd need to check it and set partial_flags as we -+ * do in alloc_l[34]_table(). -+ * -+ * Note on the use of ASSERT: if it's non-null and -+ * hasn't been cleaned up yet, it should have -+ * PGT_partial set; and so the type will be cleaned up -+ * on domain destruction. Unfortunately, we would -+ * leak the general ref held by old_guest_table; but -+ * leaking a page is less bad than a host crash. -+ */ -+ ASSERT(current->arch.old_guest_table == NULL); - page->nr_validated_ptes = i; - page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; -@@ -1607,6 +1621,7 @@ static int alloc_l3_table(struct page_info *page) - unsigned int i; - int rc = 0; - unsigned int partial_flags = page->partial_flags; -+ l3_pgentry_t l3e = l3e_empty(); - - pl3e = map_domain_page(_mfn(pfn)); - -@@ -1623,7 +1638,7 @@ static int alloc_l3_table(struct page_info *page) - for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; - i++, partial_flags = 0 ) - { -- l3_pgentry_t l3e = pl3e[i]; -+ l3e = pl3e[i]; - - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) - rc = -EINTR; -@@ -1675,6 +1690,24 @@ static int alloc_l3_table(struct page_info *page) - { - page->nr_validated_ptes = i; - page->partial_flags = partial_flags; -+ if ( current->arch.old_guest_table ) -+ { -+ /* -+ * We've experienced a validation failure. If -+ * old_guest_table is set, "transfer" the general -+ * reference count to pl3e[nr_validated_ptes] by -+ * setting PTF_partial_set. -+ * -+ * As a precaution, check that old_guest_table is the -+ * page pointed to by pl3e[nr_validated_ptes]. If -+ * not, it's safer to leak a type ref on production -+ * builds. -+ */ -+ if ( current->arch.old_guest_table == l3e_get_page(l3e) ) -+ page->partial_flags = PTF_partial_set; -+ else -+ ASSERT_UNREACHABLE(); -+ } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1851,7 +1884,23 @@ static int alloc_l4_table(struct page_info *page) - else - { - if ( current->arch.old_guest_table ) -- page->nr_validated_ptes++; -+ { -+ /* -+ * We've experienced a validation failure. If -+ * old_guest_table is set, "transfer" the general -+ * reference count to pl3e[nr_validated_ptes] by -+ * setting PTF_partial_set. -+ * -+ * As a precaution, check that old_guest_table is the -+ * page pointed to by pl4e[nr_validated_ptes]. If -+ * not, it's safer to leak a type ref on production -+ * builds. -+ */ -+ if ( current->arch.old_guest_table == l4e_get_page(l4e) ) -+ page->partial_flags = PTF_partial_set; -+ else -+ ASSERT_UNREACHABLE(); -+ } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } --- -2.23.0 - diff --git a/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch b/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch deleted file mode 100644 index ad7e6fe..0000000 --- a/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch +++ /dev/null @@ -1,413 +0,0 @@ -From ea3dc624c5e6325a9c2f079e52a85965d4ab6ce8 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:50 +0100 -Subject: [PATCH 11/11] x86/mm: Don't drop a type ref unless you held a ref to - begin with - -Validation and de-validation of pagetable trees may take arbitrarily -large amounts of time, and so must be preemptible. This is indicated -by setting the PGT_partial bit in the type_info, and setting -nr_validated_entries and partial_flags appropriately. Specifically, -if the entry at [nr_validated_entries] is partially validated, -partial_flags should have the PGT_partial_set bit set, and the entry -should hold a general reference count. During de-validation, -put_page_type() is called on partially validated entries. - -Unfortunately, there are a number of issues with the current algorithm. - -First, doing a "normal" put_page_type() is not safe when no type ref -is held: there is nothing to stop another vcpu from coming along and -picking up validation again: at which point the put_page_type may drop -the only page ref on an in-use page. Some examples are listed in the -appendix. - -The core issue is that put_page_type() is being called both to clean -up PGT_partial, and to drop a type count; and has no way of knowing -which is which; and so if in between, PGT_partial is cleared, -put_page_type() will drop the type ref erroneously. - -What is needed is to distinguish between two states: -- Dropping a type ref which is held -- Cleaning up a page which has been partially de/validated - -Fix this by telling put_page_type() which of the two activities you -intend. - -When cleaning up a partial de/validation, take no action unless you -find a page partially validated. - -If put_page_type() is called without PTF_partial_set, and finds the -page in a PGT_partial state anyway, then there's certainly been a -misaccounting somewhere, and carrying on would almost certainly cause -a security issue, so crash the host instead. - -In put_page_from_lNe, pass partial_flags on to _put_page_type(). - -old_guest_table may be set either with a fully validated page (when -using the "deferred put" pattern), or with a partially validated page -(when a normal "de-validation" is interrupted, or when a validation -fails part-way through due to invalid entries). Add a flag, -old_guest_table_partial, to indicate which of these it is, and use -that to pass the appropriate flag to _put_page_type(). - -While here, delete stray trailing whitespace. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ------ -Appendix: - -Suppose page A, when interpreted as an l3 pagetable, contains all -valid entries; and suppose A[x] points to page B, which when -interpreted as an l2 pagetable, contains all valid entries. - -P1: PIN_L3_TABLE - A -> PGT_l3_table | 1 | valid - B -> PGT_l2_table | 1 | valid - -P1: UNPIN_TABLE - > Arrange to interrupt after B has been de-validated - B: - type_info -> PGT_l2_table | 0 - A: - type_info -> PGT_l3_table | 1 | partial - nr_validated_enties -> (less than x) - -P2: mod_l4_entry to point to A - > Arrange for this to be interrupted while B is being validated - B: - type_info -> PGT_l2_table | 1 | partial - (nr_validated_entires &c set as appropriate) - A: - type_info -> PGT_l3_table | 1 | partial - nr_validated_entries -> x - partial_pte = 1 - -P3: mod_l3_entry some other unrelated l3 to point to B: - B: - type_info -> PGT_l2_table | 1 - -P1: Restart UNPIN_TABLE - -At this point, since A.nr_validate_entries == x and A.partial_pte != -0, free_l3_table() will call put_page_from_l3e() on pl3e[x], dropping -its type count to 0 while it's still being pointed to by some other l3 - -A similar issue arises with old_guest_table. Consider the following -scenario: - -Suppose A is a page which, when interpreted as an l2, has valid entries -until entry x, which is invalid. - -V1: PIN_L2_TABLE(A) - - A -> PGT_l2_table | 1 | PGT_partial - V1 -> old_guest_table = A - - -V2: PIN_L2_TABLE(A) - - A -> PGT_l2_table | 1 | PGT_partial - V2 -> old_guest_table = A - - put_old_guest_table() - _put_page_type(A) - A -> PGT_l2_table | 0 - -V1: - put_old_guest_table() - _put_page_type(A) # UNDERFLOW - -Indeed, it is possible to engineer for old_guest_table for every vcpu -a guest has to point to the same page. ---- - xen/arch/x86/domain.c | 6 +++ - xen/arch/x86/mm.c | 99 +++++++++++++++++++++++++++++++----- - xen/include/asm-x86/domain.h | 4 +- - 3 files changed, 95 insertions(+), 14 deletions(-) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 59df8a6d8d..f1ae5f89f5 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -1104,9 +1104,15 @@ int arch_set_info_guest( - rc = -ERESTART; - /* Fallthrough */ - case -ERESTART: -+ /* -+ * NB that we're putting the kernel-mode table -+ * here, which we've already successfully -+ * validated above; hence partial = false; -+ */ - v->arch.old_guest_ptpg = NULL; - v->arch.old_guest_table = - pagetable_get_page(v->arch.guest_table); -+ v->arch.old_guest_table_partial = false; - v->arch.guest_table = pagetable_null(); - break; - default: -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index a432e69c74..81774368a0 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1359,10 +1359,11 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - { - current->arch.old_guest_ptpg = ptpg; - current->arch.old_guest_table = pg; -+ current->arch.old_guest_table_partial = false; - } - else - { -- rc = _put_page_type(pg, PTF_preemptible, ptpg); -+ rc = _put_page_type(pg, flags | PTF_preemptible, ptpg); - if ( likely(!rc) ) - put_page(pg); - } -@@ -1385,6 +1386,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - unsigned long mfn = l3e_get_pfn(l3e); - bool writeable = l3e_get_flags(l3e) & _PAGE_RW; - -+ ASSERT(!(flags & PTF_partial_set)); - ASSERT(!(mfn & ((1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT)) - 1))); - do { - put_data_page(mfn_to_page(_mfn(mfn)), writeable); -@@ -1397,12 +1399,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - - if ( flags & PTF_defer ) - { -+ ASSERT(!(flags & PTF_partial_set)); - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -+ current->arch.old_guest_table_partial = false; - return 0; - } - -- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, flags | PTF_preemptible, mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - -@@ -1421,12 +1425,15 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - - if ( flags & PTF_defer ) - { -+ ASSERT(!(flags & PTF_partial_set)); - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -+ current->arch.old_guest_table_partial = false; - return 0; - } - -- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, flags | PTF_preemptible, -+ mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - } -@@ -1535,6 +1542,14 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - - pl2e = map_domain_page(_mfn(pfn)); - -+ /* -+ * NB that alloc_l2_table will never set partial_pte on an l2; but -+ * free_l2_table might if a linear_pagetable entry is interrupted -+ * partway through de-validation. In that circumstance, -+ * get_page_from_l2e() will always return -EINVAL; and we must -+ * retain the type ref by doing the normal partial_flags tracking. -+ */ -+ - for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; - i++, partial_flags = 0 ) - { -@@ -1598,6 +1613,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; -+ current->arch.old_guest_table_partial = true; - } - } - if ( rc < 0 ) -@@ -1704,12 +1720,16 @@ static int alloc_l3_table(struct page_info *page) - * builds. - */ - if ( current->arch.old_guest_table == l3e_get_page(l3e) ) -+ { -+ ASSERT(current->arch.old_guest_table_partial); - page->partial_flags = PTF_partial_set; -+ } - else - ASSERT_UNREACHABLE(); - } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; -+ current->arch.old_guest_table_partial = true; - } - while ( i-- > 0 ) - pl3e[i] = unadjust_guest_l3e(pl3e[i], d); -@@ -1897,12 +1917,16 @@ static int alloc_l4_table(struct page_info *page) - * builds. - */ - if ( current->arch.old_guest_table == l4e_get_page(l4e) ) -+ { -+ ASSERT(current->arch.old_guest_table_partial); - page->partial_flags = PTF_partial_set; -+ } - else - ASSERT_UNREACHABLE(); - } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; -+ current->arch.old_guest_table_partial = true; - } - } - } -@@ -2831,6 +2855,28 @@ static int _put_page_type(struct page_info *page, unsigned int flags, - x = y; - nx = x - 1; - -+ /* -+ * Is this expected to do a full reference drop, or only -+ * cleanup partial validation / devalidation? -+ * -+ * If the former, the caller must hold a "full" type ref; -+ * which means the page must be validated. If the page is -+ * *not* fully validated, continuing would almost certainly -+ * open up a security hole. An exception to this is during -+ * domain destruction, where PGT_validated can be dropped -+ * without dropping a type ref. -+ * -+ * If the latter, do nothing unless type PGT_partial is set. -+ * If it is set, the type count must be 1. -+ */ -+ if ( !(flags & PTF_partial_set) ) -+ BUG_ON((x & PGT_partial) || -+ !((x & PGT_validated) || page_get_owner(page)->is_dying)); -+ else if ( !(x & PGT_partial) ) -+ return 0; -+ else -+ BUG_ON((x & PGT_count_mask) != 1); -+ - ASSERT((x & PGT_count_mask) != 0); - - switch ( nx & (PGT_locked | PGT_count_mask) ) -@@ -3092,17 +3138,34 @@ int put_old_guest_table(struct vcpu *v) - if ( !v->arch.old_guest_table ) - return 0; - -- switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, -- v->arch.old_guest_ptpg) ) -+ rc = _put_page_type(v->arch.old_guest_table, -+ PTF_preemptible | -+ ( v->arch.old_guest_table_partial ? -+ PTF_partial_set : 0 ), -+ v->arch.old_guest_ptpg); -+ -+ if ( rc == -ERESTART || rc == -EINTR ) - { -- case -EINTR: -- case -ERESTART: -+ v->arch.old_guest_table_partial = (rc == -ERESTART); - return -ERESTART; -- case 0: -- put_page(v->arch.old_guest_table); - } - -+ /* -+ * It shouldn't be possible for _put_page_type() to return -+ * anything else at the moment; but if it does happen in -+ * production, leaking the type ref is probably the best thing to -+ * do. Either way, drop the general ref held by old_guest_table. -+ */ -+ ASSERT(rc == 0); -+ -+ put_page(v->arch.old_guest_table); - v->arch.old_guest_table = NULL; -+ v->arch.old_guest_ptpg = NULL; -+ /* -+ * Safest default if someone sets old_guest_table without -+ * explicitly setting old_guest_table_partial. -+ */ -+ v->arch.old_guest_table_partial = true; - - return rc; - } -@@ -3253,11 +3316,11 @@ int new_guest_cr3(mfn_t mfn) - switch ( rc = put_page_and_type_preemptible(page) ) - { - case -EINTR: -- rc = -ERESTART; -- /* fallthrough */ - case -ERESTART: - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ curr->arch.old_guest_table_partial = (rc == -ERESTART); -+ rc = -ERESTART; - break; - default: - BUG_ON(rc); -@@ -3494,6 +3557,7 @@ long do_mmuext_op( - { - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ curr->arch.old_guest_table_partial = false; - } - } - } -@@ -3528,6 +3592,11 @@ long do_mmuext_op( - case -ERESTART: - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ /* -+ * EINTR means we still hold the type ref; ERESTART -+ * means PGT_partial holds the type ref -+ */ -+ curr->arch.old_guest_table_partial = (rc == -ERESTART); - rc = 0; - break; - default: -@@ -3596,11 +3665,15 @@ long do_mmuext_op( - switch ( rc = put_page_and_type_preemptible(page) ) - { - case -EINTR: -- rc = -ERESTART; -- /* fallthrough */ - case -ERESTART: - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ /* -+ * EINTR means we still hold the type ref; -+ * ERESTART means PGT_partial holds the ref -+ */ -+ curr->arch.old_guest_table_partial = (rc == -ERESTART); -+ rc = -ERESTART; - break; - default: - BUG_ON(rc); -diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h -index 214e44ce1c..2cfce7b36b 100644 ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -307,7 +307,7 @@ struct arch_domain - - struct paging_domain paging; - struct p2m_domain *p2m; -- /* To enforce lock ordering in the pod code wrt the -+ /* To enforce lock ordering in the pod code wrt the - * page_alloc lock */ - int page_alloc_unlock_level; - -@@ -581,6 +581,8 @@ struct arch_vcpu - struct page_info *old_guest_table; /* partially destructed pagetable */ - struct page_info *old_guest_ptpg; /* containing page table of the */ - /* former, if any */ -+ bool old_guest_table_partial; /* Are we dropping a type ref, or just -+ * finishing up a partial de-validation? */ - /* guest_table holds a ref to the page, and also a type-count unless - * shadow refcounts are in use */ - pagetable_t shadow_table[4]; /* (MFN) shadow(s) of guest */ --- -2.23.0 - diff --git a/xsa301-master-1.patch b/xsa301-master-1.patch deleted file mode 100644 index 54cce2c..0000000 --- a/xsa301-master-1.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 19d6330f142cb941b6340a88592e8a294de0ff8c Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 15 Oct 2019 17:10:40 +0100 -Subject: [PATCH 1/3] xen/arm: p2m: Avoid aliasing guest physical frame - -The P2M helpers implementation is quite lax and will end up to ignore -the unused top bits of a guest physical frame. - -This effectively means that p2m_set_entry() will create a mapping for a -different frame (it is always equal to gfn & (mask unused bits)). Yet -p2m->max_mapped_gfn will be updated using the original frame. - -At the moment, p2m_get_entry() and p2m_resolve_translation_fault() -assume that p2m_get_root_pointer() will always return a non-NULL pointer -when the GFN is smaller than p2m->max_mapped_gfn. - -Unfortunately, because of the aliasing described above, it would be -possible to set p2m->max_mapped_gfn high enough so it covers frame that -would lead p2m_get_root_pointer() to return NULL. - -As we don't sanity check the guest physical frame provided by a guest, a -malicious guest could craft a series of hypercalls that will hit the -BUG_ON() and therefore DoS Xen. - -To prevent aliasing, the function p2m_get_root_pointer() is now reworked -to return NULL If any of the unused top bits are not zero. The caller -can then decide what's the appropriate action to do. Since the two paths -(i.e. P2M_ROOT_PAGES == 1 and P2M_ROOT_PAGES != 1) are now very -similarly, take the opportunity to consolidate them making the code a -bit simpler. - -With this change, p2m_get_entry() will not try to insert a mapping as -the root pointer is invalid. - -Note that root_table is now switch to unsigned long as unsigned int is -not enough to hold part of a GFN. - -This is part of XSA-301. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 17 +++++------------ - 1 file changed, 5 insertions(+), 12 deletions(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index a2749d9b6f..d0045a8b28 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -229,21 +229,14 @@ void p2m_tlb_flush_sync(struct p2m_domain *p2m) - static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m, - gfn_t gfn) - { -- unsigned int root_table; -- -- if ( P2M_ROOT_PAGES == 1 ) -- return __map_domain_page(p2m->root); -+ unsigned long root_table; - - /* -- * Concatenated root-level tables. The table number will be the -- * offset at the previous level. It is not possible to -- * concatenate a level-0 root. -+ * While the root table index is the offset from the previous level, -+ * we can't use (P2M_ROOT_LEVEL - 1) because the root level might be -+ * 0. Yet we still want to check if all the unused bits are zeroed. - */ -- ASSERT(P2M_ROOT_LEVEL > 0); -- -- root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL - 1]); -- root_table &= LPAE_ENTRY_MASK; -- -+ root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL] + LPAE_SHIFT); - if ( root_table >= P2M_ROOT_PAGES ) - return NULL; - --- -2.23.0 - diff --git a/xsa301-master-2.patch b/xsa301-master-2.patch deleted file mode 100644 index baedc9c..0000000 --- a/xsa301-master-2.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 3b896936f7505e929dd869d14afcb185d0ee75f8 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 15 Oct 2019 17:10:41 +0100 -Subject: [PATCH 2/3] xen/arm: p2m: Avoid off-by-one check on - p2m->max_mapped_gfn - -The code base is using inconsistently the field p2m->max_mapped_gfn. -Some of the useres expect that p2m->max_guest_gfn contain the highest -mapped GFN while others expect highest + 1. - -p2m->max_guest_gfn is set as highest + 1, because of that the sanity -check on the GFN in p2m_resolved_translation_fault() and -p2m_get_entry() can be bypassed when GFN == p2m->max_guest_gfn. - -p2m_get_root_pointer(p2m->max_guest_gfn) may return NULL if it is -outside of address range supported and therefore the BUG_ON() could be -hit. - -The current value hold in p2m->max_mapped_gfn is inconsistent with the -expectation of the common code (see domain_get_maximum_gpfn()) and also -the documentation of the field. - -Rather than changing the check in p2m_translation_fault() and -p2m_get_entry(), p2m->max_mapped_gfn is now containing the highest -mapped GFN and the callers assuming "highest + 1" are now adjusted. - -Take the opportunity to use 1UL rather than 1 as page_order could -theoritically big enough to overflow a 32-bit integer. - -Lastly, the documentation of the field max_guest_gfn to reflect how it -is computed. - -This is part of XSA-301. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 6 +++--- - xen/include/asm-arm/p2m.h | 5 +---- - 2 files changed, 4 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index d0045a8b28..8d20d27961 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1041,7 +1041,7 @@ static int __p2m_set_entry(struct p2m_domain *p2m, - p2m_write_pte(entry, pte, p2m->clean_pte); - - p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn, -- gfn_add(sgfn, 1 << page_order)); -+ gfn_add(sgfn, (1UL << page_order) - 1)); - p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn); - } - -@@ -1572,7 +1572,7 @@ int relinquish_p2m_mapping(struct domain *d) - p2m_write_lock(p2m); - - start = p2m->lowest_mapped_gfn; -- end = p2m->max_mapped_gfn; -+ end = gfn_add(p2m->max_mapped_gfn, 1); - - for ( ; gfn_x(start) < gfn_x(end); - start = gfn_next_boundary(start, order) ) -@@ -1641,7 +1641,7 @@ int p2m_cache_flush_range(struct domain *d, gfn_t *pstart, gfn_t end) - p2m_read_lock(p2m); - - start = gfn_max(start, p2m->lowest_mapped_gfn); -- end = gfn_min(end, p2m->max_mapped_gfn); -+ end = gfn_min(end, gfn_add(p2m->max_mapped_gfn, 1)); - - next_block_gfn = start; - -diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h -index 89f82df380..5fdb6e8183 100644 ---- a/xen/include/asm-arm/p2m.h -+++ b/xen/include/asm-arm/p2m.h -@@ -36,10 +36,7 @@ struct p2m_domain { - /* Current Translation Table Base Register for the p2m */ - uint64_t vttbr; - -- /* -- * Highest guest frame that's ever been mapped in the p2m -- * Only takes into account ram and foreign mapping -- */ -+ /* Highest guest frame that's ever been mapped in the p2m */ - gfn_t max_mapped_gfn; - - /* --- -2.23.0 - diff --git a/xsa301-master-3.patch b/xsa301-master-3.patch deleted file mode 100644 index 9f137b8..0000000 --- a/xsa301-master-3.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 060c2dd3b7c2674a019d94afb2b4ebf3663f6c6e Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 15 Oct 2019 17:10:42 +0100 -Subject: [PATCH 3/3] xen/arm: p2m: Don't check the return of - p2m_get_root_pointer() with BUG_ON() - -It turns out that the BUG_ON() was actually reachable with well-crafted -hypercalls. The BUG_ON() is here to prevent catch logical error, so -crashing Xen is a bit over the top. - -While all the holes should now be fixed, it would be better to downgrade -the BUG_ON() to something less fatal to prevent any more DoS. - -The BUG_ON() in p2m_get_entry() is now replaced by ASSERT_UNREACHABLE() -to catch mistake in debug build and return INVALID_MFN for production -build. The interface also requires to set page_order to give an idea of -the size of "hole". So 'level' is now set so we report a hole of size of -the an entry of the root page-table. This stays inline with what happen -when the GFN is higher than p2m->max_mapped_gfn. - -The BUG_ON() in p2m_resolve_translation_fault() is now replaced by -ASSERT_UNREACHABLE() to catch mistake in debug build and just report a -fault for producion build. - -This is part of XSA-301. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 13 +++++++++++-- - 1 file changed, 11 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 8d20d27961..ce59f2b503 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -395,7 +395,12 @@ mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn, - * the table should always be non-NULL because the gfn is below - * p2m->max_mapped_gfn and the root table pages are always present. - */ -- BUG_ON(table == NULL); -+ if ( !table ) -+ { -+ ASSERT_UNREACHABLE(); -+ level = P2M_ROOT_LEVEL; -+ goto out; -+ } - - for ( level = P2M_ROOT_LEVEL; level < 3; level++ ) - { -@@ -1196,7 +1201,11 @@ bool p2m_resolve_translation_fault(struct domain *d, gfn_t gfn) - * The table should always be non-NULL because the gfn is below - * p2m->max_mapped_gfn and the root table pages are always present. - */ -- BUG_ON(table == NULL); -+ if ( !table ) -+ { -+ ASSERT_UNREACHABLE(); -+ goto out; -+ } - - /* - * Go down the page-tables until an entry has the valid bit unset or --- -2.23.0 - diff --git a/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch b/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch deleted file mode 100644 index 5d52163..0000000 --- a/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0c9c0fbb356e3210cb77b3d738be50981b26058a Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Wed, 2 Oct 2019 13:36:59 +0200 -Subject: [PATCH 1/2] IOMMU: add missing HVM check -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fix an unguarded d->arch.hvm access in assign_device(). - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau MonnĂ© -Acked-by: Andrew Cooper - -(cherry picked from commit 41fd1009cd7416b73d745a77c24b4e8d1a296fe6) -Signed-off-by: Ian Jackson ---- - xen/drivers/passthrough/pci.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index 8108ed5f9a..d7420bd8bf 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1452,7 +1452,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - - /* Prevent device assign if mem paging or mem sharing have been - * enabled for this domain */ -- if ( unlikely(d->arch.hvm.mem_sharing_enabled || -+ if ( unlikely((is_hvm_domain(d) && -+ d->arch.hvm.mem_sharing_enabled) || - vm_event_check_ring(d->vm_event_paging) || - p2m_get_hostp2m(d)->global_logdirty) ) - return -EXDEV; --- -2.11.0 - diff --git a/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch b/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch deleted file mode 100644 index 181ece3..0000000 --- a/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch +++ /dev/null @@ -1,499 +0,0 @@ -From 278d8e585a9f110a1af0bd92a9fc43733c9c7227 Mon Sep 17 00:00:00 2001 -From: Paul Durrant -Date: Mon, 14 Oct 2019 17:52:59 +0100 -Subject: [PATCH 2/2] passthrough: quarantine PCI devices - -When a PCI device is assigned to an untrusted domain, it is possible for -that domain to program the device to DMA to an arbitrary address. The -IOMMU is used to protect the host from malicious DMA by making sure that -the device addresses can only target memory assigned to the guest. However, -when the guest domain is torn down the device is assigned back to dom0, -thus allowing any in-flight DMA to potentially target critical host data. - -This patch introduces a 'quarantine' for PCI devices using dom_io. When -the toolstack makes a device assignable (by binding it to pciback), it -will now also assign it to DOMID_IO and the device will only be assigned -back to dom0 when the device is made unassignable again. Whilst device is -assignable it will only ever transfer between dom_io and guest domains. -dom_io is actually only used as a sentinel domain for quarantining purposes; -it is not configured with any IOMMU mappings. Assignment to dom_io simply -means that the device's initiator (requestor) identifier is not present in -the IOMMU's device table and thus any DMA transactions issued will be -terminated with a fault condition. - -In addition, a fix to assignment handling is made for VT-d. Failure -during the assignment step should not lead to a device still being -associated with its prior owner. Hand the device to DomIO temporarily, -until the assignment step has completed successfully. Remove the PI -hooks from the source domain then earlier as well. - -Failure of the recovery reassign_device_ownership() may not go silent: -There e.g. may still be left over RMRR mappings in the domain assignment -to which has failed, and hence we can't allow that domain to continue -executing. - -NOTE: This patch also includes one printk() cleanup; the - "XEN_DOMCTL_assign_device: " tag is dropped in iommu_do_pci_domctl(), - since similar printk()-s elsewhere also don't log such a tag. - -This is XSA-302. - -Signed-off-by: Paul Durrant -Signed-off-by: Jan Beulich -Signed-off-by: Ian Jackson -(cherry picked from commit ec99857f59f7f06236f11ca8b0b2303e5e745cc4) ---- - tools/libxl/libxl_pci.c | 25 +++++++++++- - xen/arch/x86/mm.c | 2 + - xen/common/domctl.c | 14 ++++++- - xen/drivers/passthrough/amd/pci_amd_iommu.c | 10 ++++- - xen/drivers/passthrough/iommu.c | 9 +++++ - xen/drivers/passthrough/pci.c | 59 ++++++++++++++++++++++------- - xen/drivers/passthrough/vtd/iommu.c | 40 ++++++++++++++++--- - xen/include/xen/pci.h | 3 ++ - 8 files changed, 138 insertions(+), 24 deletions(-) - -diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c -index 88c324ea23..d6a23fb5f8 100644 ---- a/tools/libxl/libxl_pci.c -+++ b/tools/libxl/libxl_pci.c -@@ -754,6 +754,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, - libxl_device_pci *pcidev, - int rebind) - { -+ libxl_ctx *ctx = libxl__gc_owner(gc); - unsigned dom, bus, dev, func; - char *spath, *driver_path = NULL; - int rc; -@@ -779,7 +780,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, - } - if ( rc ) { - LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func); -- return 0; -+ goto quarantine; - } - - /* Check to see if there's already a driver that we need to unbind from */ -@@ -810,6 +811,19 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, - return ERROR_FAIL; - } - -+quarantine: -+ /* -+ * DOMID_IO is just a sentinel domain, without any actual mappings, -+ * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being -+ * unnecessarily denied. -+ */ -+ rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev), -+ XEN_DOMCTL_DEV_RDM_RELAXED); -+ if ( rc < 0 ) { -+ LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func); -+ return ERROR_FAIL; -+ } -+ - return 0; - } - -@@ -817,9 +831,18 @@ static int libxl__device_pci_assignable_remove(libxl__gc *gc, - libxl_device_pci *pcidev, - int rebind) - { -+ libxl_ctx *ctx = libxl__gc_owner(gc); - int rc; - char *driver_path; - -+ /* De-quarantine */ -+ rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev)); -+ if ( rc < 0 ) { -+ LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus, -+ pcidev->dev, pcidev->func); -+ return ERROR_FAIL; -+ } -+ - /* Unbind from pciback */ - if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) { - return ERROR_FAIL; -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3557cd1178..11d753d8d2 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -295,9 +295,11 @@ void __init arch_init_memory(void) - * Initialise our DOMID_IO domain. - * This domain owns I/O pages that are within the range of the page_info - * array. Mappings occur at the priv of the caller. -+ * Quarantined PCI devices will be associated with this domain. - */ - dom_io = domain_create(DOMID_IO, NULL, false); - BUG_ON(IS_ERR(dom_io)); -+ INIT_LIST_HEAD(&dom_io->arch.pdev_list); - - /* - * Initialise our COW domain. -diff --git a/xen/common/domctl.c b/xen/common/domctl.c -index d08b6274e2..e3c4be2b48 100644 ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -391,6 +391,16 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - - switch ( op->cmd ) - { -+ case XEN_DOMCTL_assign_device: -+ case XEN_DOMCTL_deassign_device: -+ if ( op->domain == DOMID_IO ) -+ { -+ d = dom_io; -+ break; -+ } -+ else if ( op->domain == DOMID_INVALID ) -+ return -ESRCH; -+ /* fall through */ - case XEN_DOMCTL_test_assign_device: - if ( op->domain == DOMID_INVALID ) - { -@@ -412,7 +422,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - - if ( !domctl_lock_acquire() ) - { -- if ( d ) -+ if ( d && d != dom_io ) - rcu_unlock_domain(d); - return hypercall_create_continuation( - __HYPERVISOR_domctl, "h", u_domctl); -@@ -1074,7 +1084,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - domctl_lock_release(); - - domctl_out_unlock_domonly: -- if ( d ) -+ if ( d && d != dom_io ) - rcu_unlock_domain(d); - - if ( copyback && __copy_to_guest(u_domctl, op, 1) ) -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 33a3798f36..15c13e1163 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -120,6 +120,10 @@ static void amd_iommu_setup_domain_device( - u8 bus = pdev->bus; - const struct domain_iommu *hd = dom_iommu(domain); - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return; -+ - BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || - !iommu->dev_table.buffer ); - -@@ -277,6 +281,10 @@ void amd_iommu_disable_domain_device(struct domain *domain, - int req_id; - u8 bus = pdev->bus; - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return; -+ - BUG_ON ( iommu->dev_table.buffer == NULL ); - req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); - dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); -@@ -363,7 +371,7 @@ static int amd_iommu_assign_device(struct domain *d, u8 devfn, - ivrs_mappings[req_id].read_permission); - } - -- return reassign_device(hardware_domain, d, devfn, pdev); -+ return reassign_device(pdev->domain, d, devfn, pdev); - } - - static void deallocate_next_page_table(struct page_info *pg, int level) -diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c -index a6697d58fb..2762e1342f 100644 ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -232,6 +232,9 @@ void iommu_teardown(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - -+ if ( d == dom_io ) -+ return; -+ - hd->status = IOMMU_STATUS_disabled; - hd->platform_ops->teardown(d); - tasklet_schedule(&iommu_pt_cleanup_tasklet); -@@ -241,6 +244,9 @@ int iommu_construct(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - -+ if ( d == dom_io ) -+ return 0; -+ - if ( hd->status == IOMMU_STATUS_initialized ) - return 0; - -@@ -521,6 +527,9 @@ int __init iommu_setup(void) - printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); - if ( iommu_enabled ) - { -+ if ( iommu_domain_init(dom_io) ) -+ panic("Could not set up quarantine\n"); -+ - printk(" - Dom0 mode: %s\n", - iommu_hwdom_passthrough ? "Passthrough" : - iommu_hwdom_strict ? "Strict" : "Relaxed"); -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index d7420bd8bf..d66a8a1daf 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1426,19 +1426,29 @@ static int iommu_remove_device(struct pci_dev *pdev) - return hd->platform_ops->remove_device(pdev->devfn, pci_to_dev(pdev)); - } - --/* -- * If the device isn't owned by the hardware domain, it means it already -- * has been assigned to other domain, or it doesn't exist. -- */ - static int device_assigned(u16 seg, u8 bus, u8 devfn) - { - struct pci_dev *pdev; -+ int rc = 0; - - pcidevs_lock(); -- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); -+ -+ pdev = pci_get_pdev(seg, bus, devfn); -+ -+ if ( !pdev ) -+ rc = -ENODEV; -+ /* -+ * If the device exists and it is not owned by either the hardware -+ * domain or dom_io then it must be assigned to a guest, or be -+ * hidden (owned by dom_xen). -+ */ -+ else if ( pdev->domain != hardware_domain && -+ pdev->domain != dom_io ) -+ rc = -EBUSY; -+ - pcidevs_unlock(); - -- return pdev ? 0 : -EBUSY; -+ return rc; - } - - static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) -@@ -1452,7 +1462,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - - /* Prevent device assign if mem paging or mem sharing have been - * enabled for this domain */ -- if ( unlikely((is_hvm_domain(d) && -+ if ( d != dom_io && -+ unlikely((is_hvm_domain(d) && - d->arch.hvm.mem_sharing_enabled) || - vm_event_check_ring(d->vm_event_paging) || - p2m_get_hostp2m(d)->global_logdirty) ) -@@ -1468,12 +1479,20 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - return rc; - } - -- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); -+ pdev = pci_get_pdev(seg, bus, devfn); -+ -+ rc = -ENODEV; - if ( !pdev ) -- { -- rc = pci_get_pdev(seg, bus, devfn) ? -EBUSY : -ENODEV; - goto done; -- } -+ -+ rc = 0; -+ if ( d == pdev->domain ) -+ goto done; -+ -+ rc = -EBUSY; -+ if ( pdev->domain != hardware_domain && -+ pdev->domain != dom_io ) -+ goto done; - - if ( pdev->msix ) - msixtbl_init(d); -@@ -1496,6 +1515,10 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - } - - done: -+ /* The device is assigned to dom_io so mark it as quarantined */ -+ if ( !rc && d == dom_io ) -+ pdev->quarantine = true; -+ - if ( !has_arch_pdevs(d) && has_iommu_pt(d) ) - iommu_teardown(d); - pcidevs_unlock(); -@@ -1508,6 +1531,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - { - const struct domain_iommu *hd = dom_iommu(d); - struct pci_dev *pdev = NULL; -+ struct domain *target; - int ret = 0; - - if ( !iommu_enabled || !hd->platform_ops ) -@@ -1518,12 +1542,16 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - if ( !pdev ) - return -ENODEV; - -+ /* De-assignment from dom_io should de-quarantine the device */ -+ target = (pdev->quarantine && pdev->domain != dom_io) ? -+ dom_io : hardware_domain; -+ - while ( pdev->phantom_stride ) - { - devfn += pdev->phantom_stride; - if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) ) - break; -- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, -+ ret = hd->platform_ops->reassign_device(d, target, devfn, - pci_to_dev(pdev)); - if ( !ret ) - continue; -@@ -1534,7 +1562,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - } - - devfn = pdev->devfn; -- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, -+ ret = hd->platform_ops->reassign_device(d, target, devfn, - pci_to_dev(pdev)); - if ( ret ) - { -@@ -1544,6 +1572,9 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - return ret; - } - -+ if ( pdev->domain == hardware_domain ) -+ pdev->quarantine = false; -+ - pdev->fault.count = 0; - - if ( !has_arch_pdevs(d) && has_iommu_pt(d) ) -@@ -1722,7 +1753,7 @@ int iommu_do_pci_domctl( - ret = hypercall_create_continuation(__HYPERVISOR_domctl, - "h", u_domctl); - else if ( ret ) -- printk(XENLOG_G_ERR "XEN_DOMCTL_assign_device: " -+ printk(XENLOG_G_ERR - "assign %04x:%02x:%02x.%u to dom%d failed (%d)\n", - seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - d->domain_id, ret); -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 1db1cd9f2d..a8d1baa064 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1338,6 +1338,10 @@ int domain_context_mapping_one( - int agaw, rc, ret; - bool_t flush_dev_iotlb; - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return 0; -+ - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - maddr = bus_to_context_maddr(iommu, bus); -@@ -1573,6 +1577,10 @@ int domain_context_unmap_one( - int iommu_domid, rc, ret; - bool_t flush_dev_iotlb; - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return 0; -+ - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - -@@ -1705,6 +1713,10 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - goto out; - } - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ goto out; -+ - /* - * if no other devices under the same iommu owned by this domain, - * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp -@@ -2441,6 +2453,15 @@ static int reassign_device_ownership( - if ( ret ) - return ret; - -+ if ( devfn == pdev->devfn ) -+ { -+ list_move(&pdev->domain_list, &dom_io->arch.pdev_list); -+ pdev->domain = dom_io; -+ } -+ -+ if ( !has_arch_pdevs(source) ) -+ vmx_pi_hooks_deassign(source); -+ - if ( !has_arch_pdevs(target) ) - vmx_pi_hooks_assign(target); - -@@ -2459,15 +2480,13 @@ static int reassign_device_ownership( - pdev->domain = target; - } - -- if ( !has_arch_pdevs(source) ) -- vmx_pi_hooks_deassign(source); -- - return ret; - } - - static int intel_iommu_assign_device( - struct domain *d, u8 devfn, struct pci_dev *pdev, u32 flag) - { -+ struct domain *s = pdev->domain; - struct acpi_rmrr_unit *rmrr; - int ret = 0, i; - u16 bdf, seg; -@@ -2510,8 +2529,8 @@ static int intel_iommu_assign_device( - } - } - -- ret = reassign_device_ownership(hardware_domain, d, devfn, pdev); -- if ( ret ) -+ ret = reassign_device_ownership(s, d, devfn, pdev); -+ if ( ret || d == dom_io ) - return ret; - - /* Setup rmrr identity mapping */ -@@ -2524,11 +2543,20 @@ static int intel_iommu_assign_device( - ret = rmrr_identity_mapping(d, 1, rmrr, flag); - if ( ret ) - { -- reassign_device_ownership(d, hardware_domain, devfn, pdev); -+ int rc; -+ -+ rc = reassign_device_ownership(d, s, devfn, pdev); - printk(XENLOG_G_ERR VTDPREFIX - " cannot map reserved region (%"PRIx64",%"PRIx64"] for Dom%d (%d)\n", - rmrr->base_address, rmrr->end_address, - d->domain_id, ret); -+ if ( rc ) -+ { -+ printk(XENLOG_ERR VTDPREFIX -+ " failed to reclaim %04x:%02x:%02x.%u from %pd (%d)\n", -+ seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), d, rc); -+ domain_crash(d); -+ } - break; - } - } -diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h -index 8b21e8dc84..a031fd6020 100644 ---- a/xen/include/xen/pci.h -+++ b/xen/include/xen/pci.h -@@ -88,6 +88,9 @@ struct pci_dev { - - nodeid_t node; /* NUMA node */ - -+ /* Device to be quarantined, don't automatically re-assign to dom0 */ -+ bool quarantine; -+ - /* Device with errata, ignore the BARs. */ - bool ignore_bars; - --- -2.11.0 - diff --git a/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch b/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch deleted file mode 100644 index afb1096..0000000 --- a/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch +++ /dev/null @@ -1,74 +0,0 @@ -From c8cb33fa64c9ccbfa2a494a9dad2e0a763c09176 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 1 Oct 2019 13:07:53 +0100 -Subject: [PATCH 1/4] xen/arm32: entry: Split __DEFINE_ENTRY_TRAP in two - -The preprocessing macro __DEFINE_ENTRY_TRAP is used to generate trap -entry function. While the macro is fairly small today, follow-up patches -will increase the size signicantly. - -In general, assembly macros are more readable as they allow you to name -parameters and avoid '\'. So the actual implementation of the trap is -now switched to an assembly macro. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm32/entry.S | 34 +++++++++++++++++++--------------- - 1 file changed, 19 insertions(+), 15 deletions(-) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 0b4cd19abd..4a762e04f1 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -126,24 +126,28 @@ abort_guest_exit_end: - skip_check: - mov pc, lr - --/* -- * Macro to define trap entry. The iflags corresponds to the list of -- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. -- */ -+ /* -+ * Macro to define trap entry. The iflags corresponds to the list of -+ * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. -+ */ -+ .macro vector trap, iflags -+ SAVE_ALL -+ cpsie \iflags -+ adr lr, return_from_trap -+ mov r0, sp -+ /* -+ * Save the stack pointer in r11. It will be restored after the -+ * trap has been handled (see return_from_trap). -+ */ -+ mov r11, sp -+ bic sp, #7 /* Align the stack pointer (noop on guest trap) */ -+ b do_trap_\trap -+ .endm -+ - #define __DEFINE_TRAP_ENTRY(trap, iflags) \ - ALIGN; \ - trap_##trap: \ -- SAVE_ALL; \ -- cpsie iflags; \ -- adr lr, return_from_trap; \ -- mov r0, sp; \ -- /* \ -- * Save the stack pointer in r11. It will be restored after the \ -- * trap has been handled (see return_from_trap). \ -- */ \ -- mov r11, sp; \ -- bic sp, #7; /* Align the stack pointer (noop on guest trap) */ \ -- b do_trap_##trap -+ vector trap, iflags - - /* Trap handler which unmask IRQ/Abort, keep FIQ masked */ - #define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) --- -2.11.0 - diff --git a/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch b/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch deleted file mode 100644 index 35f9c04..0000000 --- a/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch +++ /dev/null @@ -1,97 +0,0 @@ -From be7379207c83fa74f8a6c22a8ea213f02714776f Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 1 Oct 2019 13:15:48 +0100 -Subject: [PATCH 2/4] xen/arm32: entry: Fold the macro SAVE_ALL in the macro - vector - -Follow-up rework will require the macro vector to distinguish between -a trap from a guest vs while in the hypervisor. - -The macro SAVE_ALL already has code to distinguish between the two and -it is only called by the vector macro. So fold the former into the -latter. This will help to avoid duplicating the check. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm32/entry.S | 46 +++++++++++++++++++++++----------------------- - 1 file changed, 23 insertions(+), 23 deletions(-) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 4a762e04f1..150cbc0b4b 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -13,27 +13,6 @@ - #define RESTORE_BANKED(mode) \ - RESTORE_ONE_BANKED(SP_##mode) ; RESTORE_ONE_BANKED(LR_##mode) ; RESTORE_ONE_BANKED(SPSR_##mode) - --#define SAVE_ALL \ -- sub sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ \ -- push {r0-r12}; /* Save R0-R12 */ \ -- \ -- mrs r11, ELR_hyp; /* ELR_hyp is return address. */\ -- str r11, [sp, #UREGS_pc]; \ -- \ -- str lr, [sp, #UREGS_lr]; \ -- \ -- add r11, sp, #UREGS_kernel_sizeof+4; \ -- str r11, [sp, #UREGS_sp]; \ -- \ -- mrc CP32(r11, HSR); /* Save exception syndrome */ \ -- str r11, [sp, #UREGS_hsr]; \ -- \ -- mrs r11, SPSR_hyp; \ -- str r11, [sp, #UREGS_cpsr]; \ -- and r11, #PSR_MODE_MASK; \ -- cmp r11, #PSR_MODE_HYP; \ -- blne save_guest_regs -- - save_guest_regs: - #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR - /* -@@ -52,7 +31,7 @@ save_guest_regs: - ldr r11, =0xffffffff /* Clobber SP which is only valid for hypervisor frames. */ - str r11, [sp, #UREGS_sp] - SAVE_ONE_BANKED(SP_usr) -- /* LR_usr is the same physical register as lr and is saved in SAVE_ALL */ -+ /* LR_usr is the same physical register as lr and is saved by the caller */ - SAVE_BANKED(svc) - SAVE_BANKED(abt) - SAVE_BANKED(und) -@@ -131,7 +110,28 @@ skip_check: - * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. - */ - .macro vector trap, iflags -- SAVE_ALL -+ /* Save registers in the stack */ -+ sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ -+ push {r0-r12} /* Save R0-R12 */ -+ mrs r11, ELR_hyp /* ELR_hyp is return address */ -+ str r11, [sp, #UREGS_pc] -+ -+ str lr, [sp, #UREGS_lr] -+ -+ add r11, sp, #(UREGS_kernel_sizeof + 4) -+ -+ str r11, [sp, #UREGS_sp] -+ -+ mrc CP32(r11, HSR) /* Save exception syndrome */ -+ str r11, [sp, #UREGS_hsr] -+ -+ mrs r11, SPSR_hyp -+ str r11, [sp, #UREGS_cpsr] -+ and r11, #PSR_MODE_MASK -+ cmp r11, #PSR_MODE_HYP -+ blne save_guest_regs -+ -+ /* We are ready to handle the trap, setup the registers and jump. */ - cpsie \iflags - adr lr, return_from_trap - mov r0, sp --- -2.11.0 - diff --git a/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch deleted file mode 100644 index 5168452..0000000 --- a/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +++ /dev/null @@ -1,226 +0,0 @@ -From 098fe877967870ffda2dfd9629a5fd272f6aacdc Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Fri, 11 Oct 2019 17:49:28 +0100 -Subject: [PATCH 3/4] xen/arm32: Don't blindly unmask interrupts on trap - without a change of level - -Exception vectors will unmask interrupts regardless the state of them in -the interrupted context. - -One of the consequences is IRQ will be unmasked when receiving an -undefined instruction exception (used by WARN*) from the hypervisor. -This could result to unexpected behavior such as deadlock (if a lock was -shared with interrupts). - -In a nutshell, interrupts should only be unmasked when it is safe to do. -Xen only unmask IRQ and Abort interrupts, so the logic can stay simple. - -As vectors exceptions may be shared between guest and hypervisor, we now -need to have a different policy for the interrupts. - -On exception from hypervisor, each vector will select the list of -interrupts to inherit from the interrupted context. Any interrupts not -listed will be kept masked. - -On exception from the guest, the Abort and IRQ will be unmasked -depending on the exact vector. - -The interrupts will be kept unmasked when the vector cannot used by -either guest or hypervisor. - -Note that each vector is not anymore preceded by ALIGN. This is fine -because the alignment is already bigger than what we need. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm32/entry.S | 138 +++++++++++++++++++++++++++++++++++---------- - 1 file changed, 109 insertions(+), 29 deletions(-) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 150cbc0b4b..ec90cca093 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -4,6 +4,17 @@ - #include - #include - -+/* -+ * Short-hands to defined the interrupts (A, I, F) -+ * -+ * _ means the interrupt state will not change -+ * X means the state of interrupt X will change -+ * -+ * To be used with msr cpsr_* only -+ */ -+#define IFLAGS_AIF PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK -+#define IFLAGS_A_F PSR_ABT_MASK | PSR_FIQ_MASK -+ - #define SAVE_ONE_BANKED(reg) mrs r11, reg; str r11, [sp, #UREGS_##reg] - #define RESTORE_ONE_BANKED(reg) ldr r11, [sp, #UREGS_##reg]; msr reg, r11 - -@@ -106,10 +117,18 @@ skip_check: - mov pc, lr - - /* -- * Macro to define trap entry. The iflags corresponds to the list of -- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. -+ * Macro to define a trap entry. -+ * -+ * @guest_iflags: Optional list of interrupts to unmask when -+ * entering from guest context. As this is used with cpsie, -+ * the letter (a, i, f) should be used. -+ * -+ * @hyp_iflags: Optional list of interrupts to inherit when -+ * entering from hypervisor context. Any interrupts not -+ * listed will be kept unchanged. As this is used with cpsr_*, -+ * IFLAGS_* short-hands should be used. - */ -- .macro vector trap, iflags -+ .macro vector trap, guest_iflags=n, hyp_iflags=0 - /* Save registers in the stack */ - sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ - push {r0-r12} /* Save R0-R12 */ -@@ -127,12 +146,39 @@ skip_check: - - mrs r11, SPSR_hyp - str r11, [sp, #UREGS_cpsr] -- and r11, #PSR_MODE_MASK -- cmp r11, #PSR_MODE_HYP -- blne save_guest_regs - -+ /* -+ * We need to distinguish whether we came from guest or -+ * hypervisor context. -+ */ -+ and r0, r11, #PSR_MODE_MASK -+ cmp r0, #PSR_MODE_HYP -+ -+ bne 1f -+ /* -+ * Trap from the hypervisor -+ * -+ * Inherit the state of the interrupts from the hypervisor -+ * context. For that we need to use SPSR (stored in r11) and -+ * modify CPSR accordingly. -+ * -+ * CPSR = (CPSR & ~hyp_iflags) | (SPSR & hyp_iflags) -+ */ -+ mrs r10, cpsr -+ bic r10, r10, #\hyp_iflags -+ and r11, r11, #\hyp_iflags -+ orr r10, r10, r11 -+ msr cpsr_cx, r10 -+ b 2f -+ -+1: -+ /* Trap from the guest */ -+ bl save_guest_regs -+ .if \guest_iflags != n -+ cpsie \guest_iflags -+ .endif -+2: - /* We are ready to handle the trap, setup the registers and jump. */ -- cpsie \iflags - adr lr, return_from_trap - mov r0, sp - /* -@@ -144,20 +190,6 @@ skip_check: - b do_trap_\trap - .endm - --#define __DEFINE_TRAP_ENTRY(trap, iflags) \ -- ALIGN; \ --trap_##trap: \ -- vector trap, iflags -- --/* Trap handler which unmask IRQ/Abort, keep FIQ masked */ --#define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) -- --/* Trap handler which unmask Abort, keep IRQ/FIQ masked */ --#define DEFINE_TRAP_ENTRY_NOIRQ(trap) __DEFINE_TRAP_ENTRY(trap, a) -- --/* Trap handler which unmask IRQ, keep Abort/FIQ masked */ --#define DEFINE_TRAP_ENTRY_NOABORT(trap) __DEFINE_TRAP_ENTRY(trap, i) -- - .align 5 - GLOBAL(hyp_traps_vector) - b trap_reset /* 0x00 - Reset */ -@@ -228,14 +260,62 @@ decode_vectors: - - #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ - --DEFINE_TRAP_ENTRY(reset) --DEFINE_TRAP_ENTRY(undefined_instruction) --DEFINE_TRAP_ENTRY(hypervisor_call) --DEFINE_TRAP_ENTRY(prefetch_abort) --DEFINE_TRAP_ENTRY(guest_sync) --DEFINE_TRAP_ENTRY_NOIRQ(irq) --DEFINE_TRAP_ENTRY_NOIRQ(fiq) --DEFINE_TRAP_ENTRY_NOABORT(data_abort) -+/* Vector not used by the Hypervisor. */ -+trap_reset: -+ vector reset -+ -+/* -+ * Vector only used by the Hypervisor. -+ * -+ * While the exception can be executed with all the interrupts (e.g. -+ * IRQ) unmasked, the interrupted context may have purposefully masked -+ * some of them. So we want to inherit the state from the interrupted -+ * context. -+ */ -+trap_undefined_instruction: -+ vector undefined_instruction, hyp_iflags=IFLAGS_AIF -+ -+/* We should never reach this trap */ -+trap_hypervisor_call: -+ vector hypervisor_call -+ -+/* -+ * Vector only used by the hypervisor. -+ * -+ * While the exception can be executed with all the interrupts (e.g. -+ * IRQ) unmasked, the interrupted context may have purposefully masked -+ * some of them. So we want to inherit the state from the interrupted -+ * context. -+ */ -+trap_prefetch_abort: -+ vector prefetch_abort, hyp_iflags=IFLAGS_AIF -+ -+/* -+ * Vector only used by the hypervisor. -+ * -+ * Data Abort should be rare and most likely fatal. It is best to not -+ * unmask any interrupts to limit the amount of code that can run before -+ * the Data Abort is treated. -+ */ -+trap_data_abort: -+ vector data_abort -+ -+/* Vector only used by the guest. We can unmask Abort/IRQ. */ -+trap_guest_sync: -+ vector guest_sync, guest_iflags=ai -+ -+ -+/* Vector used by the hypervisor and the guest. */ -+trap_irq: -+ vector irq, guest_iflags=a, hyp_iflags=IFLAGS_A_F -+ -+/* -+ * Vector used by the hypervisor and the guest. -+ * -+ * FIQ are not meant to happen, so we don't unmask any interrupts. -+ */ -+trap_fiq: -+ vector fiq - - return_from_trap: - /* --- -2.11.0 - diff --git a/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch deleted file mode 100644 index 106cbf9..0000000 --- a/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +++ /dev/null @@ -1,114 +0,0 @@ -From c6d290ce157a044dec417fdda8db71e41a37d744 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 7 Oct 2019 18:10:56 +0100 -Subject: [PATCH 4/4] xen/arm64: Don't blindly unmask interrupts on trap - without a change of level - -Some of the traps without a change of the level (i.e. hypervisor -> -hypervisor) will unmask interrupts regardless the state of them in the -interrupted context. - -One of the consequences is IRQ will be unmasked when receiving a -synchronous exception (used by WARN*()). This could result to unexpected -behavior such as deadlock (if a lock was shared with interrupts). - -In a nutshell, interrupts should only be unmasked when it is safe to -do. Xen only unmask IRQ and Abort interrupts, so the logic can stay -simple: - - hyp_error: All the interrupts are now kept masked. SError should - be pretty rare and if ever happen then we most likely want to - avoid any other interrupts to be generated. The potential main - "caller" is during virtual SError synchronization on the exit - path from the guest (see check_pending_vserror). - - - hyp_sync: The interrupts state is inherited from the interrupted - context. - - - hyp_irq: All the interrupts but IRQ state are inherited from the - interrupted context. IRQ is kept masked. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm64/entry.S | 47 ++++++++++++++++++++++++++++++++++++++++++---- - 1 file changed, 43 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index 2d9a2713a1..3e41ba65b6 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -188,24 +188,63 @@ hyp_error_invalid: - entry hyp=1 - invalid BAD_ERROR - -+/* -+ * SError received while running in the hypervisor mode. -+ * -+ * Technically, we could unmask the IRQ if it were unmasked in the -+ * interrupted context. However, this require to check the PSTATE. For -+ * simplicity, as SError should be rare and potentially fatal, -+ * all interrupts are kept masked. -+ */ - hyp_error: - entry hyp=1 -- msr daifclr, #2 - mov x0, sp - bl do_trap_hyp_serror - exit hyp=1 - --/* Traps taken in Current EL with SP_ELx */ -+/* -+ * Synchronous exception received while running in the hypervisor mode. -+ * -+ * While the exception could be executed with all the interrupts (e.g. -+ * IRQ) unmasked, the interrupted context may have purposefully masked -+ * some of them. So we want to inherit the state from the interrupted -+ * context. -+ */ - hyp_sync: - entry hyp=1 -- msr daifclr, #6 -+ -+ /* Inherit interrupts */ -+ mrs x0, SPSR_el2 -+ and x0, x0, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK) -+ msr daif, x0 -+ - mov x0, sp - bl do_trap_hyp_sync - exit hyp=1 - -+/* -+ * IRQ received while running in the hypervisor mode. -+ * -+ * While the exception could be executed with all the interrupts but IRQ -+ * unmasked, the interrupted context may have purposefully masked some -+ * of them. So we want to inherit the state from the interrupt context -+ * and keep IRQ masked. -+ * -+ * XXX: We may want to consider an ordering between interrupts (e.g. if -+ * SError are masked, then IRQ should be masked too). However, this -+ * would require some rework in some paths (e.g. panic, livepatch) to -+ * ensure the ordering is enforced everywhere. -+ */ - hyp_irq: - entry hyp=1 -- msr daifclr, #4 -+ -+ /* Inherit D, A, F interrupts and keep I masked */ -+ mrs x0, SPSR_el2 -+ mov x1, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_FIQ_MASK) -+ and x0, x0, x1 -+ orr x0, x0, #PSR_IRQ_MASK -+ msr daif, x0 -+ - mov x0, sp - bl do_trap_irq - exit hyp=1 --- -2.11.0 - diff --git a/xsa304-4.12-1.patch b/xsa304-4.12-1.patch deleted file mode 100644 index c2ed2c2..0000000 --- a/xsa304-4.12-1.patch +++ /dev/null @@ -1,71 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtd: Hide superpage support for SandyBridge IOMMUs - -Something causes SandyBridge IOMMUs to choke when sharing EPT pagetables, and -an EPT superpage gets shattered. The root cause is still under investigation, -but the end result is unusable in combination with CVE-2018-12207 protections. - -This is part of XSA-304 / CVE-2018-12207 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/drivers/passthrough/vtd/extern.h b/xen/drivers/passthrough/vtd/extern.h -index 16eada9fa2..a71c8b0f84 100644 ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -97,6 +97,8 @@ void vtd_ops_postamble_quirk(struct iommu* iommu); - int __must_check me_wifi_quirk(struct domain *domain, - u8 bus, u8 devfn, int map); - void pci_vtd_quirk(const struct pci_dev *); -+void quirk_iommu_caps(struct iommu *iommu); -+ - bool_t platform_supports_intremap(void); - bool_t platform_supports_x2apic(void); - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index b3664ecbe0..5d34f75306 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1215,6 +1215,8 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) - if ( !(iommu->cap + 1) || !(iommu->ecap + 1) ) - return -ENODEV; - -+ quirk_iommu_caps(iommu); -+ - if ( cap_fault_reg_offset(iommu->cap) + - cap_num_fault_regs(iommu->cap) * PRIMARY_FAULT_REG_LEN >= PAGE_SIZE || - ecap_iotlb_offset(iommu->ecap) >= PAGE_SIZE ) -diff --git a/xen/drivers/passthrough/vtd/quirks.c b/xen/drivers/passthrough/vtd/quirks.c -index d6db862678..b02688e316 100644 ---- a/xen/drivers/passthrough/vtd/quirks.c -+++ b/xen/drivers/passthrough/vtd/quirks.c -@@ -540,3 +540,28 @@ void pci_vtd_quirk(const struct pci_dev *pdev) - break; - } - } -+ -+void __init quirk_iommu_caps(struct iommu *iommu) -+{ -+ /* -+ * IOMMU Quirks: -+ * -+ * SandyBridge IOMMUs claim support for 2M and 1G superpages, but don't -+ * implement superpages internally. -+ * -+ * There are issues changing the walk length under in-flight DMA, which -+ * has manifested as incompatibility between EPT/IOMMU sharing and the -+ * workaround for CVE-2018-12207 / XSA-304. Hide the superpages -+ * capabilities in the IOMMU, which will prevent Xen from sharing the EPT -+ * and IOMMU pagetables. -+ * -+ * Detection of SandyBridge unfortunately has to be done by processor -+ * model because the client parts don't expose their IOMMUs as PCI devices -+ * we could match with a Device ID. -+ */ -+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && -+ boot_cpu_data.x86 == 6 && -+ (boot_cpu_data.x86_model == 0x2a || -+ boot_cpu_data.x86_model == 0x2d) ) -+ iommu->cap &= ~(0xful << 34); -+} diff --git a/xsa304-4.12-2.patch b/xsa304-4.12-2.patch deleted file mode 100644 index 66d4301..0000000 --- a/xsa304-4.12-2.patch +++ /dev/null @@ -1,272 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtx: Disable executable EPT superpages to work around - CVE-2018-12207 - -CVE-2018-12207 covers a set of errata on various Intel processors, whereby a -machine check exception can be generated in a corner case when an executable -mapping changes size or cacheability without TLB invalidation. HVM guest -kernels can trigger this to DoS the host. - -To mitigate, in affected hardware, all EPT superpages are marked NX. When an -instruction fetch violation is observed against the superpage, the superpage -is shattered to 4k and has execute permissions restored. This prevents the -guest kernel from being able to create the necessary preconditions in the iTLB -to exploit the vulnerability. - -This does come with a workload-dependent performance overhead, caused by -increased TLB pressure. Performance can be restored, if guest kernels are -trusted not to mount an attack, by specifying ept=exec-sp on the command line. - -This is part of XSA-304 / CVE-2018-12207 - -Signed-off-by: Andrew Cooper -Acked-by: George Dunlap -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 85081fdc94..e283017015 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -895,7 +895,7 @@ Controls for interacting with the system Extended Firmware Interface. - uncacheable. - - ### ept --> `= List of [ ad=, pml= ]` -+> `= List of [ ad=, pml=, exec-sp= ]` - - > Applicability: Intel - -@@ -926,6 +926,16 @@ introduced with the Nehalem architecture. - disable PML. `pml=0` can be used to prevent the use of PML on otherwise - capable hardware. - -+* The `exec-sp` boolean controls whether EPT superpages with execute -+ permissions are permitted. In general this is good for performance. -+ -+ However, on processors vulnerable CVE-2018-12207, HVM guest kernels can -+ use executable superpages to crash the host. By default, executable -+ superpages are disabled on affected hardware. -+ -+ If HVM guest kernels are trusted not to mount a DoS against the system, -+ this option can enabled to regain performance. -+ - ### extra_guest_irqs - > `= [][,]` - -diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c -index 2089a77270..84191d4e4b 100644 ---- a/xen/arch/x86/hvm/hvm.c -+++ b/xen/arch/x86/hvm/hvm.c -@@ -1814,6 +1814,24 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, - break; - } - -+ /* -+ * Workaround for XSA-304 / CVE-2018-12207. If we take an execution -+ * fault against a non-executable superpage, shatter it to regain -+ * execute permissions. -+ */ -+ if ( page_order > 0 && npfec.insn_fetch && npfec.present && !violation ) -+ { -+ int res = p2m_set_entry(p2m, _gfn(gfn), mfn, PAGE_ORDER_4K, -+ p2mt, p2ma); -+ -+ if ( res ) -+ printk(XENLOG_ERR "Failed to shatter gfn %"PRI_gfn": %d\n", -+ gfn, res); -+ -+ rc = !res; -+ goto out_put_gfn; -+ } -+ - if ( violation ) - { - /* Should #VE be emulated for this fault? */ -diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c -index 56519fee84..ec5ab860ad 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -67,6 +67,7 @@ integer_param("ple_window", ple_window); - - static bool __read_mostly opt_ept_pml = true; - static s8 __read_mostly opt_ept_ad = -1; -+int8_t __read_mostly opt_ept_exec_sp = -1; - - static int __init parse_ept_param(const char *s) - { -@@ -82,6 +83,8 @@ static int __init parse_ept_param(const char *s) - opt_ept_ad = val; - else if ( (val = parse_boolean("pml", s, ss)) >= 0 ) - opt_ept_pml = val; -+ else if ( (val = parse_boolean("exec-sp", s, ss)) >= 0 ) -+ opt_ept_exec_sp = val; - else - rc = -EINVAL; - -diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c -index 26b7ddb5fe..28cba8ec28 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -2445,6 +2445,102 @@ static void pi_notification_interrupt(struct cpu_user_regs *regs) - static void __init lbr_tsx_fixup_check(void); - static void __init bdw_erratum_bdf14_fixup_check(void); - -+/* -+ * Calculate whether the CPU is vulnerable to Instruction Fetch page -+ * size-change MCEs. -+ */ -+static bool __init has_if_pschange_mc(void) -+{ -+ uint64_t caps = 0; -+ -+ /* -+ * If we are virtualised, there is nothing we can do. Our EPT tables are -+ * shadowed by our hypervisor, and not walked by hardware. -+ */ -+ if ( cpu_has_hypervisor ) -+ return false; -+ -+ if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) -+ rdmsrl(MSR_ARCH_CAPABILITIES, caps); -+ -+ if ( caps & ARCH_CAPS_IF_PSCHANGE_MC_NO ) -+ return false; -+ -+ /* -+ * IF_PSCHANGE_MC is only known to affect Intel Family 6 processors at -+ * this time. -+ */ -+ if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || -+ boot_cpu_data.x86 != 6 ) -+ return false; -+ -+ switch ( boot_cpu_data.x86_model ) -+ { -+ /* -+ * Core processors since at least Nehalem are vulnerable. -+ */ -+ case 0x1f: /* Auburndale / Havendale */ -+ case 0x1e: /* Nehalem */ -+ case 0x1a: /* Nehalem EP */ -+ case 0x2e: /* Nehalem EX */ -+ case 0x25: /* Westmere */ -+ case 0x2c: /* Westmere EP */ -+ case 0x2f: /* Westmere EX */ -+ case 0x2a: /* SandyBridge */ -+ case 0x2d: /* SandyBridge EP/EX */ -+ case 0x3a: /* IvyBridge */ -+ case 0x3e: /* IvyBridge EP/EX */ -+ case 0x3c: /* Haswell */ -+ case 0x3f: /* Haswell EX/EP */ -+ case 0x45: /* Haswell D */ -+ case 0x46: /* Haswell H */ -+ case 0x3d: /* Broadwell */ -+ case 0x47: /* Broadwell H */ -+ case 0x4f: /* Broadwell EP/EX */ -+ case 0x56: /* Broadwell D */ -+ case 0x4e: /* Skylake M */ -+ case 0x5e: /* Skylake D */ -+ case 0x55: /* Skylake-X / Cascade Lake */ -+ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ -+ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ -+ return true; -+ -+ /* -+ * Atom processors are not vulnerable. -+ */ -+ case 0x1c: /* Pineview */ -+ case 0x26: /* Lincroft */ -+ case 0x27: /* Penwell */ -+ case 0x35: /* Cloverview */ -+ case 0x36: /* Cedarview */ -+ case 0x37: /* Baytrail / Valleyview (Silvermont) */ -+ case 0x4d: /* Avaton / Rangely (Silvermont) */ -+ case 0x4c: /* Cherrytrail / Brasswell */ -+ case 0x4a: /* Merrifield */ -+ case 0x5a: /* Moorefield */ -+ case 0x5c: /* Goldmont */ -+ case 0x5d: /* SoFIA 3G Granite/ES2.1 */ -+ case 0x65: /* SoFIA LTE AOSP */ -+ case 0x5f: /* Denverton */ -+ case 0x6e: /* Cougar Mountain */ -+ case 0x75: /* Lightning Mountain */ -+ case 0x7a: /* Gemini Lake */ -+ case 0x86: /* Jacobsville */ -+ -+ /* -+ * Knights processors are not vulnerable. -+ */ -+ case 0x57: /* Knights Landing */ -+ case 0x85: /* Knights Mill */ -+ return false; -+ -+ default: -+ printk("Unrecognised CPU model %#x - assuming vulnerable to IF_PSCHANGE_MC\n", -+ boot_cpu_data.x86_model); -+ return true; -+ } -+} -+ - const struct hvm_function_table * __init start_vmx(void) - { - set_in_cr4(X86_CR4_VMXE); -@@ -2465,6 +2561,17 @@ const struct hvm_function_table * __init start_vmx(void) - */ - if ( cpu_has_vmx_ept && (cpu_has_vmx_pat || opt_force_ept) ) - { -+ bool cpu_has_bug_pschange_mc = has_if_pschange_mc(); -+ -+ if ( opt_ept_exec_sp == -1 ) -+ { -+ /* Default to non-executable superpages on vulnerable hardware. */ -+ opt_ept_exec_sp = !cpu_has_bug_pschange_mc; -+ -+ if ( cpu_has_bug_pschange_mc ) -+ printk("VMX: Disabling executable EPT superpages due to CVE-2018-12207\n"); -+ } -+ - vmx_function_table.hap_supported = 1; - vmx_function_table.altp2m_supported = 1; - -diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c -index 952ebad82f..834d4798c8 100644 ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -174,6 +174,12 @@ static void ept_p2m_type_to_flags(struct p2m_domain *p2m, ept_entry_t *entry, - break; - } - -+ /* -+ * Don't create executable superpages if we need to shatter them to -+ * protect against CVE-2018-12207. -+ */ -+ if ( !opt_ept_exec_sp && is_epte_superpage(entry) ) -+ entry->x = 0; - } - - #define GUEST_TABLE_MAP_FAILED 0 -diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h -index ebaa74449b..371b912887 100644 ---- a/xen/include/asm-x86/hvm/vmx/vmx.h -+++ b/xen/include/asm-x86/hvm/vmx/vmx.h -@@ -28,6 +28,8 @@ - #include - #include - -+extern int8_t opt_ept_exec_sp; -+ - typedef union { - struct { - u64 r : 1, /* bit 0 - Read permission */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 637259bd1f..32746aa8ae 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -52,6 +52,7 @@ - #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) - #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) -+#define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) diff --git a/xsa304-4.12-3.patch b/xsa304-4.12-3.patch deleted file mode 100644 index 04b4c45..0000000 --- a/xsa304-4.12-3.patch +++ /dev/null @@ -1,108 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtx: Allow runtime modification of the exec-sp setting - -See patch for details. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: George Dunlap - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index e283017015..84221fe60a 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -936,6 +936,21 @@ introduced with the Nehalem architecture. - If HVM guest kernels are trusted not to mount a DoS against the system, - this option can enabled to regain performance. - -+ This boolean may be modified at runtime using `xl set-parameters -+ ept=[no-]exec-sp` to switch between fast and secure. -+ -+ * When switching from secure to fast, preexisting HVM domains will run -+ at their current performance until they are rebooted; new domains will -+ run without any overhead. -+ -+ * When switching from fast to secure, all HVM domains will immediately -+ suffer a performance penalty. -+ -+ **Warning: No guarantee is made that this runtime option will be retained -+ indefinitely, or that it will retain this exact behaviour. It is -+ intended as an emergency option for people who first chose fast, then -+ change their minds to secure, and wish not to reboot.** -+ - ### extra_guest_irqs - > `= [][,]` - -diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c -index ec5ab860ad..c4d8a5ba78 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -95,6 +95,41 @@ static int __init parse_ept_param(const char *s) - } - custom_param("ept", parse_ept_param); - -+static int parse_ept_param_runtime(const char *s) -+{ -+ int val; -+ -+ if ( !cpu_has_vmx_ept || !hvm_funcs.hap_supported || -+ !(hvm_funcs.hap_capabilities & -+ (HVM_HAP_SUPERPAGE_2MB | HVM_HAP_SUPERPAGE_1GB)) ) -+ { -+ printk("VMX: EPT not available, or not in use - ignoring\n"); -+ return 0; -+ } -+ -+ if ( (val = parse_boolean("exec-sp", s, NULL)) < 0 ) -+ return -EINVAL; -+ -+ if ( val != opt_ept_exec_sp ) -+ { -+ struct domain *d; -+ -+ opt_ept_exec_sp = val; -+ -+ rcu_read_lock(&domlist_read_lock); -+ for_each_domain ( d ) -+ if ( paging_mode_hap(d) ) -+ p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_rw); -+ rcu_read_unlock(&domlist_read_lock); -+ } -+ -+ printk("VMX: EPT executable superpages %sabled\n", -+ val ? "en" : "dis"); -+ -+ return 0; -+} -+custom_runtime_only_param("ept", parse_ept_param_runtime); -+ - /* Dynamic (run-time adjusted) execution control flags. */ - u32 vmx_pin_based_exec_control __read_mostly; - u32 vmx_cpu_based_exec_control __read_mostly; -diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c -index f518f86493..16608098b1 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -289,15 +289,20 @@ static void change_entry_type_global(struct p2m_domain *p2m, - p2m_type_t ot, p2m_type_t nt) - { - p2m->change_entry_type_global(p2m, ot, nt); -- p2m->global_logdirty = (nt == p2m_ram_logdirty); -+ /* Don't allow 'recalculate' operations to change the logdirty state. */ -+ if ( ot != nt ) -+ p2m->global_logdirty = (nt == p2m_ram_logdirty); - } - -+/* -+ * May be called with ot = nt = p2m_ram_rw for its side effect of -+ * recalculating all PTEs in the p2m. -+ */ - void p2m_change_entry_type_global(struct domain *d, - p2m_type_t ot, p2m_type_t nt) - { - struct p2m_domain *hostp2m = p2m_get_hostp2m(d); - -- ASSERT(ot != nt); - ASSERT(p2m_is_changeable(ot) && p2m_is_changeable(nt)); - - p2m_lock(hostp2m); diff --git a/xsa305-4.12-1.patch b/xsa305-4.12-1.patch deleted file mode 100644 index e1a91a5..0000000 --- a/xsa305-4.12-1.patch +++ /dev/null @@ -1,288 +0,0 @@ -From: Andrew Cooper -Subject: x86/tsx: Introduce tsx= to use MSR_TSX_CTRL when available - -To protect against the TSX Async Abort speculative vulnerability, Intel have -released new microcode for affected parts which introduce the MSR_TSX_CTRL -control, which allows TSX to be turned off. This will be architectural on -future parts. - -Introduce tsx= to provide a global on/off for TSX, including its enumeration -via CPUID. Provide stub virtualisation of this MSR, as it is not exposed to -guests at the moment. - -VMs may have booted before microcode is loaded, or before hosts have rebooted, -and they still want to migrate freely. A VM which booted seeing TSX can -migrate safely to hosts with TSX disabled - TSX will start unconditionally -aborting, but still behave in a manner compatible with the ABI. - -The guest-visible behaviour is equivalent to late loading the microcode and -setting the RTM_DISABLE bit in the course of live patching. - -This is part of XSA-305 / CVE-2019-11135 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index e283017015..b7e1bf8e8b 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2033,6 +2033,20 @@ Xen version. - ### tsc (x86) - > `= unstable | skewed | stable:socket` - -+### tsx -+ = -+ -+ Applicability: x86 -+ Default: true -+ -+Controls for the use of Transactional Synchronization eXtensions. -+ -+On Intel parts released in Q3 2019 (with updated microcode), and future parts, -+a control has been introduced which allows TSX to be turned off. -+ -+On systems with the ability to turn TSX off, this boolean offers system wide -+control of whether TSX is enabled or disabled. -+ - ### ucode (x86) - > `= [ | scan]` - -diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile -index 8a8d8f060f..9b9a4435fb 100644 ---- a/xen/arch/x86/Makefile -+++ b/xen/arch/x86/Makefile -@@ -66,6 +66,7 @@ obj-y += sysctl.o - obj-y += time.o - obj-y += trace.o - obj-y += traps.o -+obj-y += tsx.o - obj-y += usercopy.o - obj-y += x86_emulate.o - obj-$(CONFIG_TBOOT) += tboot.o -diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c -index 57e80694f2..1727497459 100644 ---- a/xen/arch/x86/cpuid.c -+++ b/xen/arch/x86/cpuid.c -@@ -524,6 +524,20 @@ void recalculate_cpuid_policy(struct domain *d) - if ( cpu_has_itsc && (d->disable_migrate || d->arch.vtsc) ) - __set_bit(X86_FEATURE_ITSC, max_fs); - -+ /* -+ * On hardware with MSR_TSX_CTRL, the admin may have elected to disable -+ * TSX and hide the feature bits. Migrating-in VMs may have been booted -+ * pre-mitigation when the TSX features were visbile. -+ * -+ * This situation is compatible (albeit with a perf hit to any TSX code in -+ * the guest), so allow the feature bits to remain set. -+ */ -+ if ( cpu_has_tsx_ctrl ) -+ { -+ __set_bit(X86_FEATURE_HLE, max_fs); -+ __set_bit(X86_FEATURE_RTM, max_fs); -+ } -+ - /* Clamp the toolstacks choices to reality. */ - for ( i = 0; i < ARRAY_SIZE(fs); i++ ) - fs[i] &= max_fs[i]; -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 56de0fe9e1..c2722d7c73 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -132,6 +132,7 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_FLUSH_CMD: - /* Write-only */ - case MSR_TSX_FORCE_ABORT: -+ case MSR_TSX_CTRL: - /* Not offered to guests. */ - goto gp_fault; - -@@ -260,6 +261,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_ARCH_CAPABILITIES: - /* Read-only */ - case MSR_TSX_FORCE_ABORT: -+ case MSR_TSX_CTRL: - /* Not offered to guests. */ - goto gp_fault; - -diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c -index cf790f36ef..c1c7c44000 100644 ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -1594,6 +1594,8 @@ void __init noreturn __start_xen(unsigned long mbi_p) - - early_microcode_init(); - -+ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ -+ - identify_cpu(&boot_cpu_data); - - set_in_cr4(X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT); -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index 737a44f055..e21cf0a310 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -376,6 +376,8 @@ void start_secondary(void *unused) - if ( boot_cpu_has(X86_FEATURE_IBRSB) ) - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); - -+ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ -+ - if ( xen_guest ) - hypervisor_ap_setup(); - -diff --git a/xen/arch/x86/tsx.c b/xen/arch/x86/tsx.c -new file mode 100644 -index 0000000000..a8ec2ccc69 ---- /dev/null -+++ b/xen/arch/x86/tsx.c -@@ -0,0 +1,74 @@ -+#include -+#include -+ -+/* -+ * Valid values: -+ * 1 => Explicit tsx=1 -+ * 0 => Explicit tsx=0 -+ * -1 => Default, implicit tsx=1 -+ * -+ * This is arranged such that the bottom bit encodes whether TSX is actually -+ * disabled, while identifying various explicit (>=0) and implicit (<0) -+ * conditions. -+ */ -+int8_t __read_mostly opt_tsx = -1; -+int8_t __read_mostly cpu_has_tsx_ctrl = -1; -+ -+static int __init parse_tsx(const char *s) -+{ -+ int rc = 0, val = parse_bool(s, NULL); -+ -+ if ( val >= 0 ) -+ opt_tsx = val; -+ else -+ rc = -EINVAL; -+ -+ return rc; -+} -+custom_param("tsx", parse_tsx); -+ -+void tsx_init(void) -+{ -+ /* -+ * This function is first called between microcode being loaded, and CPUID -+ * being scanned generally. Calculate from raw data whether MSR_TSX_CTRL -+ * is available. -+ */ -+ if ( unlikely(cpu_has_tsx_ctrl < 0) ) -+ { -+ uint64_t caps = 0; -+ -+ if ( boot_cpu_data.cpuid_level >= 7 && -+ (cpuid_count_edx(7, 0) & cpufeat_mask(X86_FEATURE_ARCH_CAPS)) ) -+ rdmsrl(MSR_ARCH_CAPABILITIES, caps); -+ -+ cpu_has_tsx_ctrl = !!(caps & ARCH_CAPS_TSX_CTRL); -+ } -+ -+ if ( cpu_has_tsx_ctrl ) -+ { -+ uint64_t val; -+ -+ rdmsrl(MSR_TSX_CTRL, val); -+ -+ val &= ~(TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR); -+ /* Check bottom bit only. Higher bits are various sentinals. */ -+ if ( !(opt_tsx & 1) ) -+ val |= TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR; -+ -+ wrmsrl(MSR_TSX_CTRL, val); -+ } -+ else if ( opt_tsx >= 0 ) -+ printk_once(XENLOG_WARNING -+ "MSR_TSX_CTRL not available - Ignoring tsx= setting\n"); -+} -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * tab-width: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 32746aa8ae..d5f3899f73 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -53,6 +53,7 @@ - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) - #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) - #define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) -+#define ARCH_CAPS_TSX_CTRL (_AC(1, ULL) << 7) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) -@@ -60,6 +61,10 @@ - #define MSR_TSX_FORCE_ABORT 0x0000010f - #define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) - -+#define MSR_TSX_CTRL 0x00000122 -+#define TSX_CTRL_RTM_DISABLE (_AC(1, ULL) << 0) -+#define TSX_CTRL_CPUID_CLEAR (_AC(1, ULL) << 1) -+ - /* Intel MSRs. Some also available on other CPUs */ - #define MSR_IA32_PERFCTR0 0x000000c1 - #define MSR_IA32_A_PERFCTR0 0x000004c1 -diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h -index d33ac34d29..1b52712180 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -263,6 +263,16 @@ static always_inline unsigned int cpuid_count_ebx( - return ebx; - } - -+static always_inline unsigned int cpuid_count_edx( -+ unsigned int leaf, unsigned int subleaf) -+{ -+ unsigned int edx, tmp; -+ -+ cpuid_count(leaf, subleaf, &tmp, &tmp, &tmp, &edx); -+ -+ return edx; -+} -+ - static inline unsigned long read_cr0(void) - { - unsigned long cr0; -@@ -609,6 +619,9 @@ static inline uint8_t get_cpu_family(uint32_t raw, uint8_t *model, - return fam; - } - -+extern int8_t opt_tsx, cpu_has_tsx_ctrl; -+void tsx_init(void); -+ - #endif /* !__ASSEMBLY__ */ - - #endif /* __ASM_X86_PROCESSOR_H */ -diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h -index 89939f43c8..6529f12dae 100644 ---- a/xen/include/xen/lib.h -+++ b/xen/include/xen/lib.h -@@ -114,6 +114,16 @@ extern int printk_ratelimit(void); - #define gprintk(lvl, fmt, args...) \ - printk(XENLOG_GUEST lvl "%pv " fmt, current, ## args) - -+#define printk_once(fmt, args...) \ -+({ \ -+ static bool __read_mostly once_; \ -+ if ( unlikely(!once_) ) \ -+ { \ -+ once_ = true; \ -+ printk(fmt, ## args); \ -+ } \ -+}) -+ - #ifdef NDEBUG - - static inline void diff --git a/xsa305-4.12-2.patch b/xsa305-4.12-2.patch deleted file mode 100644 index 07fba86..0000000 --- a/xsa305-4.12-2.patch +++ /dev/null @@ -1,192 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel - -See patch documentation and comments. - -This is part of XSA-305 / CVE-2019-11135 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index b7e1bf8e8b..74e1e35b88 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1920,7 +1920,7 @@ extreme care.** - An overall boolean value, `spec-ctrl=no`, can be specified to turn off all - mitigations, including pieces of infrastructure used to virtualise certain - mitigation features for guests. This also includes settings which `xpti`, --`smt`, `pv-l1tf` control, unless the respective option(s) have been -+`smt`, `pv-l1tf`, `tsx` control, unless the respective option(s) have been - specified earlier on the command line. - - Alternatively, a slightly more restricted `spec-ctrl=no-xen` can be used to -@@ -2037,7 +2037,7 @@ Xen version. - = - - Applicability: x86 -- Default: true -+ Default: false on parts vulnerable to TAA, true otherwise - - Controls for the use of Transactional Synchronization eXtensions. - -@@ -2047,6 +2047,19 @@ a control has been introduced which allows TSX to be turned off. - On systems with the ability to turn TSX off, this boolean offers system wide - control of whether TSX is enabled or disabled. - -+On parts vulnerable to CVE-2019-11135 / TSX Asynchronous Abort, the following -+logic applies: -+ -+ * An explicit `tsx=` choice is honoured, even if it is `true` and would -+ result in a vulnerable system. -+ -+ * When no explicit `tsx=` choice is given, parts vulnerable to TAA will be -+ mitigated by disabling TSX, as this is the lowest overhead option. -+ -+ * If the use of TSX is important, the more expensive TAA mitigations can be -+ opted in to with `smt=0 spec-ctrl=md-clear`, at which point TSX will remain -+ active by default. -+ - ### ucode (x86) - > `= [ | scan]` - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index b37d40e643..800139d79c 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -96,6 +96,9 @@ static int __init parse_spec_ctrl(const char *s) - if ( opt_pv_l1tf_domu < 0 ) - opt_pv_l1tf_domu = 0; - -+ if ( opt_tsx == -1 ) -+ opt_tsx = -3; -+ - disable_common: - opt_rsb_pv = false; - opt_rsb_hvm = false; -@@ -306,7 +309,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - printk("Speculative mitigation facilities:\n"); - - /* Hardware features which pertain to speculative mitigations. */ -- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", -@@ -318,7 +321,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", - (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", - (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", -- (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); -+ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : "", -+ (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", -+ (caps & ARCH_CAPS_TAA_NO) ? " TAA_NO" : ""); - - /* Compiled-in support which pertains to mitigations. */ - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) -@@ -332,7 +337,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s, Other:%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -341,6 +346,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", - !boot_cpu_has(X86_FEATURE_SSBD) ? "" : - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", -+ !(caps & ARCH_CAPS_TSX_CTRL) ? "" : -+ (opt_tsx & 1) ? " TSX+" : " TSX-", - opt_ibpb ? " IBPB" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", - opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); -@@ -862,6 +869,7 @@ void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; - bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; -+ bool cpu_has_bug_taa; - uint64_t caps = 0; - - if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) -@@ -1086,6 +1094,53 @@ void __init init_speculation_mitigations(void) - "enabled. Mitigations will not be fully effective. Please\n" - "choose an explicit smt= setting. See XSA-297.\n"); - -+ /* -+ * Vulnerability to TAA is a little complicated to quantify. -+ * -+ * In the pipeline, it is just another way to get speculative access to -+ * stale load port, store buffer or fill buffer data, and therefore can be -+ * considered a superset of MDS (on TSX-capable parts). On parts which -+ * predate MDS_NO, the existing VERW flushing will mitigate this -+ * sidechannel as well. -+ * -+ * On parts which contain MDS_NO, the lack of VERW flushing means that an -+ * attacker can still use TSX to target microarchitectural buffers to leak -+ * secrets. Therefore, we consider TAA to be the set of TSX-capable parts -+ * which have MDS_NO but lack TAA_NO. -+ * -+ * Note: cpu_has_rtm (== hle) could already be hidden by `tsx=0` on the -+ * cmdline. MSR_TSX_CTRL will only appear on TSX-capable parts, so -+ * we check both to spot TSX in a microcode/cmdline independent way. -+ */ -+ cpu_has_bug_taa = -+ (cpu_has_rtm || (caps & ARCH_CAPS_TSX_CTRL)) && -+ (caps & (ARCH_CAPS_MDS_NO | ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO; -+ -+ /* -+ * On TAA-affected hardware, disabling TSX is the preferred mitigation, vs -+ * the MDS mitigation of disabling HT and using VERW flushing. -+ * -+ * On CPUs which advertise MDS_NO, VERW has no flushing side effect until -+ * the TSX_CTRL microcode is loaded, despite the MD_CLEAR CPUID bit being -+ * advertised, and there isn't a MD_CLEAR_2 flag to use... -+ * -+ * If we're on affected hardware, able to do something about it (which -+ * implies that VERW now works), no explicit TSX choice and traditional -+ * MDS mitigations (no-SMT, VERW) not obviosuly in use (someone might -+ * plausibly value TSX higher than Hyperthreading...), disable TSX to -+ * mitigate TAA. -+ */ -+ if ( opt_tsx == -1 && cpu_has_bug_taa && (caps & ARCH_CAPS_TSX_CTRL) && -+ ((hw_smt_enabled && opt_smt) || -+ !boot_cpu_has(X86_FEATURE_SC_VERW_IDLE)) ) -+ { -+ setup_clear_cpu_cap(X86_FEATURE_HLE); -+ setup_clear_cpu_cap(X86_FEATURE_RTM); -+ -+ opt_tsx = 0; -+ tsx_init(); -+ } -+ - print_details(thunk, caps); - - /* -diff --git a/xen/arch/x86/tsx.c b/xen/arch/x86/tsx.c -index a8ec2ccc69..2d202a0d4e 100644 ---- a/xen/arch/x86/tsx.c -+++ b/xen/arch/x86/tsx.c -@@ -5,7 +5,8 @@ - * Valid values: - * 1 => Explicit tsx=1 - * 0 => Explicit tsx=0 -- * -1 => Default, implicit tsx=1 -+ * -1 => Default, implicit tsx=1, may change to 0 to mitigate TAA -+ * -3 => Implicit tsx=1 (feed-through from spec-ctrl=0) - * - * This is arranged such that the bottom bit encodes whether TSX is actually - * disabled, while identifying various explicit (>=0) and implicit (<0) -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index d5f3899f73..3971b992d3 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -54,6 +54,7 @@ - #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) - #define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) - #define ARCH_CAPS_TSX_CTRL (_AC(1, ULL) << 7) -+#define ARCH_CAPS_TAA_NO (_AC(1, ULL) << 8) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) diff --git a/xsa306-4.12.patch b/xsa306-4.12.patch deleted file mode 100644 index 13147b5..0000000 --- a/xsa306-4.12.patch +++ /dev/null @@ -1,91 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: default to always quarantining PCI devices - -XSA-302 relies on the use of libxl's "assignable-add" feature to prepare -devices to be assigned to untrusted guests. - -Unfortunately, this is not considered a strictly required step for -device assignment. The PCI passthrough documentation on the wiki -describes alternate ways of preparing devices for assignment, and -libvirt uses its own ways as well. Hosts where these alternate methods -are used will still leave the system in a vulnerable state after the -device comes back from a guest. - -Default to always quarantining PCI devices, but provide a command line -option to revert back to prior behavior (such that people who both -sufficiently trust their guests and want to be able to use devices in -Dom0 again after they had been in use by a guest wouldn't need to -"manually" move such devices back from DomIO to Dom0). - -This is XSA-306. - -Reported-by: Marek Marczykowski-GĂ³recki -Signed-off-by: Jan Beulich -Reviewed-by: Wei Liu - ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1171,7 +1171,7 @@ detection of systems known to misbehave - > Default: `new` unless directed-EOI is supported - - ### iommu -- = List of [ , verbose, debug, force, required, -+ = List of [ , verbose, debug, force, required, quarantine, - sharept, intremap, intpost, crash-disable, - snoop, qinval, igfx, amd-iommu-perdev-intremap, - dom0-{passthrough,strict} ] -@@ -1209,6 +1209,12 @@ boolean (e.g. `iommu=no`) can override t - will prevent Xen from booting if IOMMUs aren't discovered and enabled - successfully. - -+* The `quarantine` boolean can be used to control Xen's behavior when -+ de-assigning devices from guests. If enabled (the default), Xen always -+ quarantines such devices; they must be explicitly assigned back to Dom0 -+ before they can be used there again. If disabled, Xen will only -+ quarantine devices the toolstack hass arranged for getting quarantined. -+ - * The `sharept` boolean controls whether the IOMMU pagetables are shared - with the CPU-side HAP pagetables, or allocated separately. Sharing - reduces the memory overhead, but doesn't work in combination with CPU-side ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -30,6 +30,7 @@ bool_t __initdata iommu_enable = 1; - bool_t __read_mostly iommu_enabled; - bool_t __read_mostly force_iommu; - bool_t __read_mostly iommu_verbose; -+bool __read_mostly iommu_quarantine = true; - bool_t __read_mostly iommu_igfx = 1; - bool_t __read_mostly iommu_snoop = 1; - bool_t __read_mostly iommu_qinval = 1; -@@ -74,6 +75,8 @@ static int __init parse_iommu_param(cons - else if ( (val = parse_boolean("force", s, ss)) >= 0 || - (val = parse_boolean("required", s, ss)) >= 0 ) - force_iommu = val; -+ else if ( (val = parse_boolean("quarantine", s, ss)) >= 0 ) -+ iommu_quarantine = val; - else if ( (val = parse_boolean("igfx", s, ss)) >= 0 ) - iommu_igfx = val; - else if ( (val = parse_boolean("verbose", s, ss)) >= 0 ) ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1548,7 +1548,8 @@ int deassign_device(struct domain *d, u1 - return -ENODEV; - - /* De-assignment from dom_io should de-quarantine the device */ -- target = (pdev->quarantine && pdev->domain != dom_io) ? -+ target = ((pdev->quarantine || iommu_quarantine) && -+ pdev->domain != dom_io) ? - dom_io : hardware_domain; - - while ( pdev->phantom_stride ) ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -53,7 +53,7 @@ static inline bool_t dfn_eq(dfn_t x, dfn - } - - extern bool_t iommu_enable, iommu_enabled; --extern bool_t force_iommu, iommu_verbose, iommu_igfx; -+extern bool force_iommu, iommu_quarantine, iommu_verbose, iommu_igfx; - extern bool_t iommu_snoop, iommu_qinval, iommu_intremap, iommu_intpost; - extern bool_t iommu_hap_pt_share; - extern bool_t iommu_debug; diff --git a/xsa307.patch b/xsa307.patch deleted file mode 100644 index 82b4ada..0000000 --- a/xsa307.patch +++ /dev/null @@ -1,99 +0,0 @@ -From: Jan Beulich -Subject: x86+Arm32: make find_next_{,zero_}bit() have well defined behavior - -These functions getting used with the 2nd and 3rd arguments being equal -wasn't well defined: Arm64 reliably returns the value of the 2nd -argument in this case, while on x86 for bitmaps up to 64 bits wide the -return value was undefined (due to the undefined behavior of a shift of -a value by the number of bits it's wide) when the incoming value was 64. -On Arm32 an actual out of bounds access would happen when the -size/offset value is a multiple of 32; if this access doesn't fault, the -return value would have been sufficiently correct afaict. - -Make the functions consistently tolerate the last two arguments being -equal (and in fact the 3rd argument being greater or equal to the 2nd), -in favor of finding and fixing all the use sites that violate the -original more strict assumption. - -This is XSA-307. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -The most obvious (albeit still indirect) exposure to guests is -evtchn_check_pollers(), which imo makes this a security issue at least -for Arm32. - -This was originally already discussed between (at least) Andrew and me, -and I don't really recall who brought up the issue first. - -Note that Arm's Linux origin of the code may call for syncing -publication with them. Then again I don't want to tell them just to see -them go public ahead of us. - ---- a/xen/arch/arm/arm32/lib/findbit.S -+++ b/xen/arch/arm/arm32/lib/findbit.S -@@ -42,8 +42,8 @@ ENDPROC(_find_first_zero_bit_le) - * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) - */ - ENTRY(_find_next_zero_bit_le) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - ARM( ldrb r3, [r0, r2, lsr #3] ) -@@ -83,8 +83,8 @@ ENDPROC(_find_first_bit_le) - * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) - */ - ENTRY(_find_next_bit_le) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - ARM( ldrb r3, [r0, r2, lsr #3] ) -@@ -117,8 +117,8 @@ ENTRY(_find_first_zero_bit_be) - ENDPROC(_find_first_zero_bit_be) - - ENTRY(_find_next_zero_bit_be) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - eor r3, r2, #0x18 @ big endian byte ordering -@@ -151,8 +151,8 @@ ENTRY(_find_first_bit_be) - ENDPROC(_find_first_bit_be) - - ENTRY(_find_next_bit_be) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - eor r3, r2, #0x18 @ big endian byte ordering ---- a/xen/include/asm-x86/bitops.h -+++ b/xen/include/asm-x86/bitops.h -@@ -358,7 +358,7 @@ static always_inline unsigned int __scan - const unsigned long *a__ = (addr); \ - unsigned int s__ = (size); \ - unsigned int o__ = (off); \ -- if ( __builtin_constant_p(size) && !s__ ) \ -+ if ( o__ >= s__ ) \ - r__ = s__; \ - else if ( __builtin_constant_p(size) && s__ <= BITS_PER_LONG ) \ - r__ = o__ + __scanbit(*(const unsigned long *)(a__) >> o__, s__); \ -@@ -390,7 +390,7 @@ static always_inline unsigned int __scan - const unsigned long *a__ = (addr); \ - unsigned int s__ = (size); \ - unsigned int o__ = (off); \ -- if ( __builtin_constant_p(size) && !s__ ) \ -+ if ( o__ >= s__ ) \ - r__ = s__; \ - else if ( __builtin_constant_p(size) && s__ <= BITS_PER_LONG ) \ - r__ = o__ + __scanbit(~*(const unsigned long *)(a__) >> o__, s__); \ diff --git a/xsa308.patch b/xsa308.patch deleted file mode 100644 index 7abe3ef..0000000 --- a/xsa308.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtx: Work around SingleStep + STI/MovSS VMEntry failures - -See patch comment for technical details. - -Concerning the timeline, this was first discovered in the aftermath of -XSA-156 which caused #DB to be intercepted unconditionally, but only in -its SingleStep + STI form which is restricted to privileged software. - -After working with Intel and identifying the problematic vmentry check, -this workaround was suggested, and the patch was posted in an RFC -series. Outstanding work for that series (not breaking Introspection) -is still pending, and this fix from it (which wouldn't have been good -enough in its original form) wasn't committed. - -A vmentry failure was reported to xen-devel, and debugging identified -this bug in its SingleStep + MovSS form by way of INT1, which does not -involve the use of any privileged instructions, and proving this to be a -security issue. - -This is XSA-308 - -Reported-by: HĂ¥kon Alstadheim -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Acked-by: Kevin Tian - -diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c -index 6a5eeb5c13..59b836f43f 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -3816,6 +3816,42 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs) - HVMTRACE_1D(TRAP_DEBUG, exit_qualification); - __restore_debug_registers(v); - write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE); -+ -+ /* -+ * Work around SingleStep + STI/MovSS VMEntry failures. -+ * -+ * We intercept #DB unconditionally to work around CVE-2015-8104 / -+ * XSA-156 (guest-kernel induced host DoS). -+ * -+ * STI/MovSS shadows block/defer interrupts/exceptions (exact -+ * details are complicated and poorly documented). Debug -+ * exceptions delayed for any reason are stored in the -+ * PENDING_DBG_EXCEPTIONS field. -+ * -+ * The falling edge of PENDING_DBG causes #DB to be delivered, -+ * resulting in a VMExit, as #DB is intercepted. The VMCS still -+ * reports blocked-by-STI/MovSS. -+ * -+ * The VMEntry checks when EFLAGS.TF is set don't like a VMCS in -+ * this state. Despite a #DB queued in VMENTRY_INTR_INFO, the -+ * state is rejected as DR6.BS isn't pending. Fix this up. -+ */ -+ if ( unlikely(regs->eflags & X86_EFLAGS_TF) ) -+ { -+ unsigned long int_info; -+ -+ __vmread(GUEST_INTERRUPTIBILITY_INFO, &int_info); -+ -+ if ( int_info & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) ) -+ { -+ unsigned long pending_dbg; -+ -+ __vmread(GUEST_PENDING_DBG_EXCEPTIONS, &pending_dbg); -+ __vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, -+ pending_dbg | DR_STEP); -+ } -+ } -+ - if ( !v->domain->debugger_attached ) - { - unsigned long insn_len = 0; diff --git a/xsa309.patch b/xsa309.patch deleted file mode 100644 index 8bd9237..0000000 --- a/xsa309.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 523e3974ed2213719a19218f5b246e382ceef18a Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Wed, 30 Oct 2019 17:05:28 +0000 -Subject: [PATCH] x86/mm: Don't reset linear_pt_count on partial validation - -"Linear pagetables" is a technique which involves either pointing a -pagetable at itself, or to another pagetable the same or higher level. -Xen has limited support for linear pagetables: A page may either point -to itself, or point to another page of the same level (i.e., L2 to L2, -L3 to L3, and so on). - -XSA-240 introduced an additional restriction that limited the "depth" -of such chains by allowing pages to either *point to* other pages of -the same level, or *be pointed to* by other pages of the same level, -but not both. To implement this, we keep track of the number of -outstanding times a page points to or is pointed to another page -table, to prevent both from happening at the same time. - -Unfortunately, the original commit introducing this reset this count -when resuming validation of a partially-validated pagetable, dropping -some "linear_pt_entry" counts. - -On debug builds on systems where guests used this feature, this might -lead to crashes that look like this: - - Assertion 'oc > 0' failed at mm.c:874 - -Worse, if an attacker could engineer such a situation to occur, they -might be able to make loops or other abitrary chains of linear -pagetables, leading to the denial-of-service situation outlined in -XSA-240. - -This is XSA-309. - -Reported-by: Manuel Bouyer -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 7d4dd80a85..01393fb0da 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3059,8 +3059,8 @@ static int _get_page_type(struct page_info *page, unsigned long type, - { - page->nr_validated_ptes = 0; - page->partial_flags = 0; -+ page->linear_pt_count = 0; - } -- page->linear_pt_count = 0; - rc = alloc_page_type(page, type, preemptible); - } - --- -2.24.0 - diff --git a/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch b/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch deleted file mode 100644 index 3eb3533..0000000 --- a/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 7c537dc8d28a03064a14171ed5c6fc329531816a Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Tue, 19 Nov 2019 11:40:34 +0000 -Subject: [PATCH 1/3] x86/mm: Set old_guest_table when destroying vcpu - pagetables - -Changeset 6c4efc1eba ("x86/mm: Don't drop a type ref unless you held a -ref to begin with"), part of XSA-299, changed the calling discipline -of put_page_type() such that if put_page_type() returned -ERESTART -(indicating a partially de-validated page), subsequent calls to -put_page_type() must be called with PTF_partial_set. If called on a -partially de-validated page but without PTF_partial_set, Xen will -BUG(), because to do otherwise would risk opening up the kind of -privilege escalation bug described in XSA-299. - -One place this was missed was in vcpu_destroy_pagetables(). -put_page_and_type_preemptible() is called, but on -ERESTART, the -entire operation is simply restarted, causing put_page_type() to be -called on a partially de-validated page without PTF_partial_set. The -result was that if such an operation were interrupted, Xen would hit a -BUG(). - -Fix this by having vcpu_destroy_pagetables() consistently pass off -interrupted de-validations to put_old_page_type(): -- Unconditionally clear references to the page, even if - put_page_and_type failed -- Set old_guest_table and old_guest_table_partial appropriately - -While here, do some refactoring: - - - Move clearing of arch.cr3 to the top of the function - - - Now that clearing is unconditional, move the unmap to the same - conditional as the l4tab mapping. This also allows us to reduce - the scope of the l4tab variable. - - - Avoid code duplication by looping to drop references on - guest_table_user - -This is part of XSA-310. - -Reported-by: Sarah Newman -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- -Added in v2. - -Changes in v3: -- Minor comment / whitespace fixes ---- - xen/arch/x86/mm.c | 75 +++++++++++++++++++++++++++++------------------ - 1 file changed, 47 insertions(+), 28 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 01393fb0da..a759afc9e3 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3142,40 +3142,36 @@ int put_old_guest_table(struct vcpu *v) - int vcpu_destroy_pagetables(struct vcpu *v) - { - unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); -- struct page_info *page; -- l4_pgentry_t *l4tab = NULL; -+ struct page_info *page = NULL; - int rc = put_old_guest_table(v); -+ bool put_guest_table_user = false; - - if ( rc ) - return rc; - -+ v->arch.cr3 = 0; -+ -+ /* -+ * Get the top-level guest page; either the guest_table itself, for -+ * 64-bit, or the top-level l4 entry for 32-bit. Either way, remove -+ * the reference to that page. -+ */ - if ( is_pv_32bit_vcpu(v) ) - { -- l4tab = map_domain_page(_mfn(mfn)); -- mfn = l4e_get_pfn(*l4tab); -- } -+ l4_pgentry_t *l4tab = map_domain_page(_mfn(mfn)); - -- if ( mfn ) -- { -- page = mfn_to_page(_mfn(mfn)); -- if ( paging_mode_refcounts(v->domain) ) -- put_page(page); -- else -- rc = put_page_and_type_preemptible(page); -- } -- -- if ( l4tab ) -- { -- if ( !rc ) -- l4e_write(l4tab, l4e_empty()); -+ mfn = l4e_get_pfn(*l4tab); -+ l4e_write(l4tab, l4e_empty()); - unmap_domain_page(l4tab); - } -- else if ( !rc ) -+ else - { - v->arch.guest_table = pagetable_null(); -+ put_guest_table_user = true; -+ } - -- /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ -- mfn = pagetable_get_pfn(v->arch.guest_table_user); -+ /* Free that page if non-zero */ -+ do { - if ( mfn ) - { - page = mfn_to_page(_mfn(mfn)); -@@ -3183,18 +3179,41 @@ int vcpu_destroy_pagetables(struct vcpu *v) - put_page(page); - else - rc = put_page_and_type_preemptible(page); -+ mfn = 0; - } -- if ( !rc ) -- v->arch.guest_table_user = pagetable_null(); -- } - -- v->arch.cr3 = 0; -+ if ( !rc && put_guest_table_user ) -+ { -+ /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ -+ mfn = pagetable_get_pfn(v->arch.guest_table_user); -+ v->arch.guest_table_user = pagetable_null(); -+ put_guest_table_user = false; -+ } -+ } while ( mfn ); - - /* -- * put_page_and_type_preemptible() is liable to return -EINTR. The -- * callers of us expect -ERESTART so convert it over. -+ * If a "put" operation was interrupted, finish things off in -+ * put_old_guest_table() when the operation is restarted. - */ -- return rc != -EINTR ? rc : -ERESTART; -+ switch ( rc ) -+ { -+ case -EINTR: -+ case -ERESTART: -+ v->arch.old_guest_ptpg = NULL; -+ v->arch.old_guest_table = page; -+ v->arch.old_guest_table_partial = (rc == -ERESTART); -+ rc = -ERESTART; -+ break; -+ default: -+ /* -+ * Failure to 'put' a page may cause it to leak, but that's -+ * less bad than a crash. -+ */ -+ ASSERT(rc == 0); -+ break; -+ } -+ -+ return rc; - } - - int new_guest_cr3(mfn_t mfn) --- -2.24.0 - diff --git a/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch b/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch deleted file mode 100644 index 12c04e4..0000000 --- a/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 128cb126aee9b4a2855ab898fdfbfe7009fbf1f5 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 31 Oct 2019 11:17:38 +0000 -Subject: [PATCH 2/3] x86/mm: alloc/free_lN_table: Retain partial_flags on - -EINTR - -When validating or de-validating pages (in alloc_lN_table and -free_lN_table respectively), the `partial_flags` local variable is -used to keep track of whether the "current" PTE started the entire -operation in a "may be partial" state. - -One of the patches in XSA-299 addressed the fact that it is possible -for a previously-partially-validated entry to subsequently be found to -have invalid entries (indicated by returning -EINVAL); in which case -page->partial_flags needs to be set to indicate that the current PTE -may have the partial bit set (and thus _put_page_type() should be -called with PTF_partial_set). - -Unfortunately, the patches in XSA-299 assumed that once -put_page_from_lNe() returned -ERESTART on a page, it was not possible -for it to return -EINTR. This turns out to be true for -alloc_lN_table() and free_lN_table, but not for _get_page_type() and -_put_page_type(): both can return -EINTR when called on pages with -PGT_partial set. In these cases, the pages PGT_partial will still be -set; failing to set partial_flags appropriately may allow an attacker -to do a privilege escalation similar to those described in XSA-299. - -Fix this by always copying the local partial_flags variable into -page->partial_flags when exiting early. - -NB that on the "get" side, no adjustment to nr_validated_entries is -needed: whether pte[i] is partially validated or entirely -un-validated, we want nr_validated_entries = i. On the "put" side, -however, we need to adjust nr_validated_entries appropriately: if -pte[i] is entirely validated, we want nr_validated_entries = i + 1; if -pte[i] is partially validated, we want nr_validated_entries = i. - -This is part of XSA-310. - -Reported-by: Sarah Newman -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 16 ++++++++-------- - 1 file changed, 8 insertions(+), 8 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index a759afc9e3..97c8d73b7b 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1557,7 +1557,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags;; - rc = -ERESTART; - } - else if ( rc < 0 && rc != -EINTR ) -@@ -1660,7 +1660,7 @@ static int alloc_l3_table(struct page_info *page) - else if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - if ( rc < 0 ) -@@ -1982,8 +1982,8 @@ static int free_l2_table(struct page_info *page) - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { -- page->nr_validated_ptes = i + 1; -- page->partial_flags = 0; -+ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - -@@ -2030,8 +2030,8 @@ static int free_l3_table(struct page_info *page) - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { -- page->nr_validated_ptes = i + 1; -- page->partial_flags = 0; -+ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - return rc > 0 ? 0 : rc; -@@ -2061,8 +2061,8 @@ static int free_l4_table(struct page_info *page) - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { -- page->nr_validated_ptes = i + 1; -- page->partial_flags = 0; -+ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - --- -2.24.0 - diff --git a/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch b/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch deleted file mode 100644 index 9ee4238..0000000 --- a/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch +++ /dev/null @@ -1,75 +0,0 @@ -From e9f835982a726ae16997c566b5eafab74f8b4cb7 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Mon, 28 Oct 2019 14:33:51 +0000 -Subject: [PATCH 3/3] x86/mm: relinquish_memory: Grab an extra type ref when - setting PGT_partial - -The PGT_partial bit in page->type_info holds both a type count and a -general ref count. During domain tear-down, when free_page_type() -returns -ERESTART, relinquish_memory() correctly handles the general -ref count, but fails to grab an extra type count when setting -PGT_partial. When this bit is eventually cleared, type_count underflows -and triggers the following BUG in page_alloc.c:free_domheap_pages(): - - BUG_ON((pg[i].u.inuse.type_info & PGT_count_mask) != 0); - -As far as we can tell, this page underflow cannot be exploited any any -other way: The page can't be used as a pagetable by the dying domain -because it's dying; it can't be used as a pagetable by any other -domain since it belongs to the dying domain; and ownership can't -transfer to any other domain without hitting the BUG_ON() in -free_domheap_pages(). - -(steal_page() won't work on a page in this state, since it requires -PGC_allocated to be set, and PGC_allocated will already have been -cleared.) - -Fix this by grabbing an extra type ref if setting PGT_partial in -relinquish_memory. - -This is part of XSA-310. - -Reported-by: Sarah Newman -Signed-off-by: George Dunlap -Acked-by: Jan Beulich ---- -v2: -- Move discussion of potential exploits into the commit message -- Keep PGT_partial and put_page() ordering ---- - xen/arch/x86/domain.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index f1dd86e12e..51880fc50d 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2049,6 +2049,25 @@ static int relinquish_memory( - goto out; - case -ERESTART: - page_list_add(page, list); -+ /* -+ * PGT_partial holds a type ref and a general ref. -+ * If we came in with PGT_partial set, then we 1) -+ * don't need to grab an extra type count, and 2) -+ * do need to drop the extra page ref we grabbed -+ * at the top of the loop. If we didn't come in -+ * with PGT_partial set, we 1) do need to drab an -+ * extra type count, but 2) can transfer the page -+ * ref we grabbed above to it. -+ * -+ * Note that we must increment type_info before -+ * setting PGT_partial. Theoretically it should -+ * be safe to drop the page ref before setting -+ * PGT_partial, but do it afterwards just to be -+ * extra safe. -+ */ -+ if ( !(x & PGT_partial) ) -+ page->u.inuse.type_info++; -+ smp_wmb(); - page->u.inuse.type_info |= PGT_partial; - if ( x & PGT_partial ) - put_page(page); --- -2.24.0 - diff --git a/xsa311-4.12.patch b/xsa311-4.12.patch deleted file mode 100644 index 6c18188..0000000 --- a/xsa311-4.12.patch +++ /dev/null @@ -1,194 +0,0 @@ -From: Andrew Cooper -Subject: AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables - -update_paging_mode() has multiple bugs: - - 1) Booting with iommu=debug will cause it to inform you that that it called - without the pdev_list lock held. - 2) When growing by more than a single level, it leaks the newly allocated - table(s) in the case of a further error. - -Furthermore, the choice of default level for a domain has issues: - - 1) All HVM guests grow from 2 to 3 levels during construction because of the - position of the VRAM just below the 4G boundary, so defaulting to 2 is a - waste of effort. - 2) The limit for PV guests doesn't take memory hotplug into account, and - isn't dynamic at runtime like HVM guests. This means that a PV guest may - get RAM which it can't map in the IOMMU. - -The dynamic height is a property unique to AMD, and adds a substantial -quantity of complexity for what is a marginal performance improvement. Remove -the complexity by removing the dynamic height. - -PV guests now get 3 or 4 levels based on any hotplug regions in the host. -This only makes a difference for hardware which previously had all RAM below -the 512G boundary, and a hotplug region above. - -HVM guests now get 4 levels (which will be sufficient until 256TB guests -become a thing), because we don't currently have the information to know when -3 would be safe to use. - -The overhead of this extra level is not expected to be noticeable. It costs -one page (4k) per domain, and one extra IO-TLB paging structure cache entry -which is very hot and less likely to be evicted. - -This is XSA-311. - -Reported-by: XXX PERSON 3 -Signed-off-by: Andrew Cooper -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 628aa60230..9a222c95e1 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -455,100 +455,6 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - return 0; - } - --static int update_paging_mode(struct domain *d, unsigned long dfn) --{ -- uint16_t bdf; -- void *device_entry; -- unsigned int req_id, level, offset; -- unsigned long flags; -- struct pci_dev *pdev; -- struct amd_iommu *iommu = NULL; -- struct page_info *new_root = NULL; -- struct page_info *old_root = NULL; -- void *new_root_vaddr; -- unsigned long old_root_mfn; -- struct domain_iommu *hd = dom_iommu(d); -- -- if ( dfn == dfn_x(INVALID_DFN) ) -- return -EADDRNOTAVAIL; -- ASSERT(!(dfn >> DEFAULT_DOMAIN_ADDRESS_WIDTH)); -- -- level = hd->arch.paging_mode; -- old_root = hd->arch.root_table; -- offset = dfn >> (PTE_PER_TABLE_SHIFT * (level - 1)); -- -- ASSERT(spin_is_locked(&hd->arch.mapping_lock) && is_hvm_domain(d)); -- -- while ( offset >= PTE_PER_TABLE_SIZE ) -- { -- /* Allocate and install a new root table. -- * Only upper I/O page table grows, no need to fix next level bits */ -- new_root = alloc_amd_iommu_pgtable(); -- if ( new_root == NULL ) -- { -- AMD_IOMMU_DEBUG("%s Cannot allocate I/O page table\n", -- __func__); -- return -ENOMEM; -- } -- -- new_root_vaddr = __map_domain_page(new_root); -- old_root_mfn = mfn_x(page_to_mfn(old_root)); -- set_iommu_pde_present(new_root_vaddr, old_root_mfn, level, -- !!IOMMUF_writable, !!IOMMUF_readable); -- level++; -- old_root = new_root; -- offset >>= PTE_PER_TABLE_SHIFT; -- unmap_domain_page(new_root_vaddr); -- } -- -- if ( new_root != NULL ) -- { -- hd->arch.paging_mode = level; -- hd->arch.root_table = new_root; -- -- if ( !pcidevs_locked() ) -- AMD_IOMMU_DEBUG("%s Try to access pdev_list " -- "without aquiring pcidevs_lock.\n", __func__); -- -- /* Update device table entries using new root table and paging mode */ -- for_each_pdev( d, pdev ) -- { -- if ( pdev->type == DEV_TYPE_PCI_HOST_BRIDGE ) -- continue; -- -- bdf = PCI_BDF2(pdev->bus, pdev->devfn); -- iommu = find_iommu_for_device(pdev->seg, bdf); -- if ( !iommu ) -- { -- AMD_IOMMU_DEBUG("%s Fail to find iommu.\n", __func__); -- return -ENODEV; -- } -- -- spin_lock_irqsave(&iommu->lock, flags); -- do { -- req_id = get_dma_requestor_id(pdev->seg, bdf); -- device_entry = iommu->dev_table.buffer + -- (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); -- -- /* valid = 0 only works for dom0 passthrough mode */ -- amd_iommu_set_root_page_table((uint32_t *)device_entry, -- page_to_maddr(hd->arch.root_table), -- d->domain_id, -- hd->arch.paging_mode, 1); -- -- amd_iommu_flush_device(iommu, req_id); -- bdf += pdev->phantom_stride; -- } while ( PCI_DEVFN2(bdf) != pdev->devfn && -- PCI_SLOT(bdf) == PCI_SLOT(pdev->devfn) ); -- spin_unlock_irqrestore(&iommu->lock, flags); -- } -- -- /* For safety, invalidate all entries */ -- amd_iommu_flush_all_pages(d); -- } -- return 0; --} -- - int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, - unsigned int flags, unsigned int *flush_flags) - { -@@ -573,20 +479,6 @@ int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, - return rc; - } - -- /* Since HVM domain is initialized with 2 level IO page table, -- * we might need a deeper page table for wider dfn now */ -- if ( is_hvm_domain(d) ) -- { -- if ( update_paging_mode(d, dfn_x(dfn)) ) -- { -- spin_unlock(&hd->arch.mapping_lock); -- AMD_IOMMU_DEBUG("Update page mode failed dfn = %"PRI_dfn"\n", -- dfn_x(dfn)); -- domain_crash(d); -- return -EFAULT; -- } -- } -- - if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, true) || (pt_mfn[1] == 0) ) - { - spin_unlock(&hd->arch.mapping_lock); -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 15c13e1163..57dc2c5f20 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -242,10 +242,17 @@ static int amd_iommu_domain_init(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - -- /* For pv and dom0, stick with get_paging_mode(max_page) -- * For HVM dom0, use 2 level page table at first */ -- hd->arch.paging_mode = is_hvm_domain(d) ? -- 2 : amd_iommu_get_paging_mode(max_page); -+ /* -+ * Choose the number of levels for the IOMMU page tables. -+ * - PV needs 3 or 4, depending on whether there is RAM (including hotplug -+ * RAM) above the 512G boundary. -+ * - HVM could in principle use 3 or 4 depending on how much guest -+ * physical address space we give it, but this isn't known yet so use 4 -+ * unilaterally. -+ */ -+ hd->arch.paging_mode = is_hvm_domain(d) -+ ? 4 : amd_iommu_get_paging_mode(get_upper_mfn_bound()); -+ - return 0; - } - From d759d346c3d8965faebe9d7f1176831cef7e14d5 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 Jan 2020 21:38:40 +0000 Subject: [PATCH 02/11] arm: a CPU may speculate past the ERET instruction [XSA-312] --- xen.spec | 7 +++- xsa312.patch | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 xsa312.patch diff --git a/xen.spec b/xen.spec index 8dc8bbc..38a86a6 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.2 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,6 +114,7 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch Patch54: xen.python38.patch +Patch55: xsa312.patch %if %build_qemutrad @@ -312,6 +313,7 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch54 -p1 +%patch55 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -875,6 +877,9 @@ fi %endif %changelog +* Tue Jan 14 2020 Michael Young - 4.12.2-2 +- arm: a CPU may speculate past the ERET instruction [XSA-312] + * Mon Jan 06 2020 Michael Young - 4.12.2-1 - update to 4.12.2 remove patches for issues now fixed upstream diff --git a/xsa312.patch b/xsa312.patch new file mode 100644 index 0000000..ae3fa40 --- /dev/null +++ b/xsa312.patch @@ -0,0 +1,93 @@ +From 9f807cf84a9a7a011cf1df7895c54d6031a7596d Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Thu, 19 Dec 2019 08:12:21 +0000 +Subject: [PATCH] xen/arm: Place a speculation barrier sequence following an + eret instruction + +Some CPUs can speculate past an ERET instruction and potentially perform +speculative accesses to memory before processing the exception return. +Since the register state is often controlled by lower privilege level +at the point of an ERET, this could potentially be used as part of a +side-channel attack. + +Newer CPUs may implement a new SB barrier instruction which acts +as an architected speculation barrier. For current CPUs, the sequence +DSB; ISB is known to prevent speculation. + +The latter sequence is heavier than SB but it would never be executed +(this is speculation after all!). + +Introduce a new macro 'sb' that could be used when a speculation barrier +is required. For now it is using dsb; isb but this could easily be +updated to cater SB in the future. + +This is XSA-312. + +Signed-off-by: Julien Grall +--- + xen/arch/arm/arm32/entry.S | 1 + + xen/arch/arm/arm64/entry.S | 3 +++ + xen/include/asm-arm/macros.h | 9 +++++++++ + 3 files changed, 13 insertions(+) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 31ccfb2631..b228d44b19 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -426,6 +426,7 @@ return_to_hypervisor: + add sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ + clrex + eret ++ sb + + /* + * struct vcpu *__context_switch(struct vcpu *prev, struct vcpu *next) +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index d35855af96..175ea2981e 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -354,6 +354,7 @@ guest_sync: + */ + mov x1, xzr + eret ++ sb + + check_wa2: + /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ +@@ -393,6 +394,7 @@ wa2_end: + #endif /* !CONFIG_ARM_SSBD */ + mov x0, xzr + eret ++ sb + guest_sync_slowpath: + /* + * x0/x1 may have been scratch by the fast path above, so avoid +@@ -457,6 +459,7 @@ return_from_trap: + ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + + eret ++ sb + + /* + * Consume pending SError generated by the guest if any. +diff --git a/xen/include/asm-arm/macros.h b/xen/include/asm-arm/macros.h +index 91ea3505e4..4833671f4c 100644 +--- a/xen/include/asm-arm/macros.h ++++ b/xen/include/asm-arm/macros.h +@@ -20,4 +20,13 @@ + .endr + .endm + ++ /* ++ * Speculative barrier ++ * XXX: Add support for the 'sb' instruction ++ */ ++ .macro sb ++ dsb nsh ++ isb ++ .endm ++ + #endif /* __ASM_ARM_MACROS_H */ +-- +2.17.1 + From ec4c2c59a0d8eb70db5b32428b2cf013b0d92e0d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 15 Apr 2020 21:22:51 +0100 Subject: [PATCH 03/11] 4 security updates multiple xenoprof issues [XSA-313, CVE-2020-11740, CVE-2020-11741] (#1823912, #1823914) Missing memory barriers in read-write unlock paths [XSA-314, CVE-2020-11739] (#1823784) Bad error path in GNTTABOP_map_grant [XSA-316, CVE-2020-11743] (#1823926) Bad continuation handling in GNTTABOP_copy [XSA-318, CVE-2020-11742] (#1823943) --- xen.spec | 21 +++++++- xsa313-1.patch | 26 +++++++++ xsa313-2.patch | 132 ++++++++++++++++++++++++++++++++++++++++++++++ xsa314-4.13.patch | 121 ++++++++++++++++++++++++++++++++++++++++++ xsa316-xen.patch | 30 +++++++++++ xsa318.patch | 39 ++++++++++++++ 6 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 xsa313-1.patch create mode 100644 xsa313-2.patch create mode 100644 xsa314-4.13.patch create mode 100644 xsa316-xen.patch create mode 100644 xsa318.patch diff --git a/xen.spec b/xen.spec index 38a86a6..7b95a20 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -115,6 +115,11 @@ Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch Patch54: xen.python38.patch Patch55: xsa312.patch +Patch56: xsa313-1.patch +Patch57: xsa313-2.patch +Patch58: xsa314-4.13.patch +Patch59: xsa316-xen.patch +Patch60: xsa318.patch %if %build_qemutrad @@ -314,6 +319,11 @@ manage Xen virtual machines. %patch43 -p1 %patch54 -p1 %patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -877,6 +887,15 @@ fi %endif %changelog +* Wed Apr 15 2020 Michael Young - 4.12.2-3 +- 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.12.2-2 - 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 ceaeefb05ac037c7c8bad15676c85562b75551b9 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 19 May 2020 20:05:03 +0100 Subject: [PATCH 04/11] update to 4.12.3 --- .gitignore | 2 +- sources | 2 +- xen.spec | 23 +++----- xsa312.patch | 93 -------------------------------- xsa313-1.patch | 26 --------- xsa313-2.patch | 132 ---------------------------------------------- xsa314-4.13.patch | 121 ------------------------------------------ xsa316-xen.patch | 30 ----------- xsa318.patch | 39 -------------- 9 files changed, 10 insertions(+), 458 deletions(-) delete mode 100644 xsa312.patch delete mode 100644 xsa313-1.patch delete mode 100644 xsa313-2.patch delete mode 100644 xsa314-4.13.patch delete mode 100644 xsa316-xen.patch delete mode 100644 xsa318.patch diff --git a/.gitignore b/.gitignore index 972c3fa..48c8352 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.12.2.tar.gz +/xen-4.12.3.tar.gz diff --git a/sources b/sources index 63327d7..5e999c3 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.12.2.tar.gz) = 7d9e7921271830c9eadf1bb8eca1aec20d343ad7475b0dc3165ef6d681759e7cb70739f8d9f85622a23aef960988820e822267fb198b12ee3dd657ad6164069f +SHA512 (xen-4.12.3.tar.gz) = 7bbf4e752477f18143ac9a62fb633b1fbe115a1a9b03d0132f33dfca025bc9b76d9c2e9b66a3e407d14aff161b940b1a82e3e3ca43213798e9dd38b6970194e0 diff --git a/xen.spec b/xen.spec index 7b95a20..d79e21c 100644 --- a/xen.spec +++ b/xen.spec @@ -1,6 +1,7 @@ # Build ocaml bits unless rpmbuild was run with --without ocaml # or ocamlopt is missing (the xen makefile doesn't build ocaml bits if it isn't there) -%define with_ocaml %{?_without_ocaml: 0} %{?!_without_ocaml: 1} +#%%define with_ocaml %%{?_without_ocaml: 0} %%{?!_without_ocaml: 1} +%define with_ocaml 0 %define build_ocaml %(test -x %{_bindir}/ocamlopt && echo %{with_ocaml} || echo 0) # Build with docs unless rpmbuild was run with --without docs %define build_docs %{?_without_docs: 0} %{?!_without_docs: 1} @@ -57,8 +58,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.12.2 -Release: 3%{?dist} +Version: 4.12.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 @@ -114,12 +115,6 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch Patch54: xen.python38.patch -Patch55: xsa312.patch -Patch56: xsa313-1.patch -Patch57: xsa313-2.patch -Patch58: xsa314-4.13.patch -Patch59: xsa316-xen.patch -Patch60: xsa318.patch %if %build_qemutrad @@ -318,12 +313,6 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -887,6 +876,10 @@ fi %endif %changelog +* Tue May 19 2020 Michael Young - 4.12.3-1 +- update to 4.12.3 + remove patches for issues now fixed upstream + * Wed Apr 15 2020 Michael Young - 4.12.2-3 - multiple xenoprof issues [XSA-313, CVE-2020-11740, CVE-2020-11741] (#1823912, #1823914) diff --git a/xsa312.patch b/xsa312.patch deleted file mode 100644 index ae3fa40..0000000 --- a/xsa312.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 9f807cf84a9a7a011cf1df7895c54d6031a7596d Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 19 Dec 2019 08:12:21 +0000 -Subject: [PATCH] xen/arm: Place a speculation barrier sequence following an - eret instruction - -Some CPUs can speculate past an ERET instruction and potentially perform -speculative accesses to memory before processing the exception return. -Since the register state is often controlled by lower privilege level -at the point of an ERET, this could potentially be used as part of a -side-channel attack. - -Newer CPUs may implement a new SB barrier instruction which acts -as an architected speculation barrier. For current CPUs, the sequence -DSB; ISB is known to prevent speculation. - -The latter sequence is heavier than SB but it would never be executed -(this is speculation after all!). - -Introduce a new macro 'sb' that could be used when a speculation barrier -is required. For now it is using dsb; isb but this could easily be -updated to cater SB in the future. - -This is XSA-312. - -Signed-off-by: Julien Grall ---- - xen/arch/arm/arm32/entry.S | 1 + - xen/arch/arm/arm64/entry.S | 3 +++ - xen/include/asm-arm/macros.h | 9 +++++++++ - 3 files changed, 13 insertions(+) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 31ccfb2631..b228d44b19 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -426,6 +426,7 @@ return_to_hypervisor: - add sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ - clrex - eret -+ sb - - /* - * struct vcpu *__context_switch(struct vcpu *prev, struct vcpu *next) -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index d35855af96..175ea2981e 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -354,6 +354,7 @@ guest_sync: - */ - mov x1, xzr - eret -+ sb - - check_wa2: - /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ -@@ -393,6 +394,7 @@ wa2_end: - #endif /* !CONFIG_ARM_SSBD */ - mov x0, xzr - eret -+ sb - guest_sync_slowpath: - /* - * x0/x1 may have been scratch by the fast path above, so avoid -@@ -457,6 +459,7 @@ return_from_trap: - ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - - eret -+ sb - - /* - * Consume pending SError generated by the guest if any. -diff --git a/xen/include/asm-arm/macros.h b/xen/include/asm-arm/macros.h -index 91ea3505e4..4833671f4c 100644 ---- a/xen/include/asm-arm/macros.h -+++ b/xen/include/asm-arm/macros.h -@@ -20,4 +20,13 @@ - .endr - .endm - -+ /* -+ * Speculative barrier -+ * XXX: Add support for the 'sb' instruction -+ */ -+ .macro sb -+ dsb nsh -+ isb -+ .endm -+ - #endif /* __ASM_ARM_MACROS_H */ --- -2.17.1 - diff --git a/xsa313-1.patch b/xsa313-1.patch deleted file mode 100644 index 95fde7e..0000000 --- a/xsa313-1.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Jan Beulich -Subject: xenoprof: clear buffer intended to be shared with guests - -alloc_xenheap_pages() making use of MEMF_no_scrub is fine for Xen -internally used allocations, but buffers allocated to be shared with -(unpriviliged) guests need to be zapped of their prior content. - -This is part of XSA-313. - -Reported-by: Ilja Van Sprundel -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Wei Liu - ---- a/xen/common/xenoprof.c -+++ b/xen/common/xenoprof.c -@@ -253,6 +253,9 @@ static int alloc_xenoprof_struct( - return -ENOMEM; - } - -+ for ( i = 0; i < npages; ++i ) -+ clear_page(d->xenoprof->rawbuf + i * PAGE_SIZE); -+ - d->xenoprof->npages = npages; - d->xenoprof->nbuf = nvcpu; - d->xenoprof->bufsize = bufsize; diff --git a/xsa313-2.patch b/xsa313-2.patch deleted file mode 100644 index d81b823..0000000 --- a/xsa313-2.patch +++ /dev/null @@ -1,132 +0,0 @@ -From: Jan Beulich -Subject: xenoprof: limit consumption of shared buffer data - -Since a shared buffer can be written to by the guest, we may only read -the head and tail pointers from there (all other fields should only ever -be written to). Furthermore, for any particular operation the two values -must be read exactly once, with both checks and consumption happening -with the thus read values. (The backtrace related xenoprof_buf_space() -use in xenoprof_log_event() is an exception: The values used there get -re-checked by every subsequent xenoprof_add_sample().) - -Since that code needed touching, also fix the double increment of the -lost samples count in case the backtrace related xenoprof_add_sample() -invocation in xenoprof_log_event() fails. - -Where code is being touched anyway, add const as appropriate, but take -the opportunity to entirely drop the now unused domain parameter of -xenoprof_buf_space(). - -This is part of XSA-313. - -Reported-by: Ilja Van Sprundel -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Wei Liu - ---- a/xen/common/xenoprof.c -+++ b/xen/common/xenoprof.c -@@ -479,25 +479,22 @@ static int add_passive_list(XEN_GUEST_HA - - - /* Get space in the buffer */ --static int xenoprof_buf_space(struct domain *d, xenoprof_buf_t * buf, int size) -+static int xenoprof_buf_space(int head, int tail, int size) - { -- int head, tail; -- -- head = xenoprof_buf(d, buf, event_head); -- tail = xenoprof_buf(d, buf, event_tail); -- - return ((tail > head) ? 0 : size) + tail - head - 1; - } - - /* Check for space and add a sample. Return 1 if successful, 0 otherwise. */ --static int xenoprof_add_sample(struct domain *d, xenoprof_buf_t *buf, -+static int xenoprof_add_sample(const struct domain *d, -+ const struct xenoprof_vcpu *v, - uint64_t eip, int mode, int event) - { -+ xenoprof_buf_t *buf = v->buffer; - int head, tail, size; - - head = xenoprof_buf(d, buf, event_head); - tail = xenoprof_buf(d, buf, event_tail); -- size = xenoprof_buf(d, buf, event_size); -+ size = v->event_size; - - /* make sure indexes in shared buffer are sane */ - if ( (head < 0) || (head >= size) || (tail < 0) || (tail >= size) ) -@@ -506,7 +503,7 @@ static int xenoprof_add_sample(struct do - return 0; - } - -- if ( xenoprof_buf_space(d, buf, size) > 0 ) -+ if ( xenoprof_buf_space(head, tail, size) > 0 ) - { - xenoprof_buf(d, buf, event_log[head].eip) = eip; - xenoprof_buf(d, buf, event_log[head].mode) = mode; -@@ -530,7 +527,6 @@ static int xenoprof_add_sample(struct do - int xenoprof_add_trace(struct vcpu *vcpu, uint64_t pc, int mode) - { - struct domain *d = vcpu->domain; -- xenoprof_buf_t *buf = d->xenoprof->vcpu[vcpu->vcpu_id].buffer; - - /* Do not accidentally write an escape code due to a broken frame. */ - if ( pc == XENOPROF_ESCAPE_CODE ) -@@ -539,7 +535,8 @@ int xenoprof_add_trace(struct vcpu *vcpu - return 0; - } - -- return xenoprof_add_sample(d, buf, pc, mode, 0); -+ return xenoprof_add_sample(d, &d->xenoprof->vcpu[vcpu->vcpu_id], -+ pc, mode, 0); - } - - void xenoprof_log_event(struct vcpu *vcpu, const struct cpu_user_regs *regs, -@@ -570,17 +567,22 @@ void xenoprof_log_event(struct vcpu *vcp - /* Provide backtrace if requested. */ - if ( backtrace_depth > 0 ) - { -- if ( (xenoprof_buf_space(d, buf, v->event_size) < 2) || -- !xenoprof_add_sample(d, buf, XENOPROF_ESCAPE_CODE, mode, -- XENOPROF_TRACE_BEGIN) ) -+ if ( xenoprof_buf_space(xenoprof_buf(d, buf, event_head), -+ xenoprof_buf(d, buf, event_tail), -+ v->event_size) < 2 ) - { - xenoprof_buf(d, buf, lost_samples)++; - lost_samples++; - return; - } -+ -+ /* xenoprof_add_sample() will increment lost_samples on failure */ -+ if ( !xenoprof_add_sample(d, v, XENOPROF_ESCAPE_CODE, mode, -+ XENOPROF_TRACE_BEGIN) ) -+ return; - } - -- if ( xenoprof_add_sample(d, buf, pc, mode, event) ) -+ if ( xenoprof_add_sample(d, v, pc, mode, event) ) - { - if ( is_active(vcpu->domain) ) - active_samples++; ---- a/xen/include/xen/xenoprof.h -+++ b/xen/include/xen/xenoprof.h -@@ -61,12 +61,12 @@ struct xenoprof { - - #ifndef CONFIG_COMPAT - #define XENOPROF_COMPAT(x) 0 --#define xenoprof_buf(d, b, field) ((b)->field) -+#define xenoprof_buf(d, b, field) ACCESS_ONCE((b)->field) - #else - #define XENOPROF_COMPAT(x) ((x)->is_compat) --#define xenoprof_buf(d, b, field) (*(!(d)->xenoprof->is_compat ? \ -- &(b)->native.field : \ -- &(b)->compat.field)) -+#define xenoprof_buf(d, b, field) ACCESS_ONCE(*(!(d)->xenoprof->is_compat \ -+ ? &(b)->native.field \ -+ : &(b)->compat.field)) - #endif - - struct domain; diff --git a/xsa314-4.13.patch b/xsa314-4.13.patch deleted file mode 100644 index 67e0066..0000000 --- a/xsa314-4.13.patch +++ /dev/null @@ -1,121 +0,0 @@ -From ab49f005f7d01d4004d76f2e295d31aca7d4f93a Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 20 Feb 2020 20:54:40 +0000 -Subject: [PATCH] xen/rwlock: Add missing memory barrier in the unlock path of - rwlock - -The rwlock unlock paths are using atomic_sub() to release the lock. -However the implementation of atomic_sub() rightfully doesn't contain a -memory barrier. On Arm, this means a processor is allowed to re-order -the memory access with the preceeding access. - -In other words, the unlock may be seen by another processor before all -the memory accesses within the "critical" section. - -The rwlock paths already contains barrier indirectly, but they are not -very useful without the counterpart in the unlock paths. - -The memory barriers are not necessary on x86 because loads/stores are -not re-ordered with lock instructions. - -So add arch_lock_release_barrier() in the unlock paths that will only -add memory barrier on Arm. - -Take the opportunity to document each lock paths explaining why a -barrier is not necessary. - -This is XSA-314. - -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Reviewed-by: Stefano Stabellini - ---- - xen/include/xen/rwlock.h | 29 ++++++++++++++++++++++++++++- - 1 file changed, 28 insertions(+), 1 deletion(-) - -diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h -index 3dfea1ac2a..516486306f 100644 ---- a/xen/include/xen/rwlock.h -+++ b/xen/include/xen/rwlock.h -@@ -48,6 +48,10 @@ static inline int _read_trylock(rwlock_t *lock) - if ( likely(!(cnts & _QW_WMASK)) ) - { - cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts); -+ /* -+ * atomic_add_return() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - if ( likely(!(cnts & _QW_WMASK)) ) - return 1; - atomic_sub(_QR_BIAS, &lock->cnts); -@@ -64,11 +68,19 @@ static inline void _read_lock(rwlock_t *lock) - u32 cnts; - - cnts = atomic_add_return(_QR_BIAS, &lock->cnts); -+ /* -+ * atomic_add_return() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - if ( likely(!(cnts & _QW_WMASK)) ) - return; - - /* The slowpath will decrement the reader count, if necessary. */ - queue_read_lock_slowpath(lock); -+ /* -+ * queue_read_lock_slowpath() is using spinlock and therefore is a -+ * full barrier. So no need for an arch_lock_acquire_barrier(). -+ */ - } - - static inline void _read_lock_irq(rwlock_t *lock) -@@ -92,6 +104,7 @@ static inline unsigned long _read_lock_irqsave(rwlock_t *lock) - */ - static inline void _read_unlock(rwlock_t *lock) - { -+ arch_lock_release_barrier(); - /* - * Atomically decrement the reader count - */ -@@ -121,11 +134,20 @@ static inline int _rw_is_locked(rwlock_t *lock) - */ - static inline void _write_lock(rwlock_t *lock) - { -- /* Optimize for the unfair lock case where the fair flag is 0. */ -+ /* -+ * Optimize for the unfair lock case where the fair flag is 0. -+ * -+ * atomic_cmpxchg() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - if ( atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0 ) - return; - - queue_write_lock_slowpath(lock); -+ /* -+ * queue_write_lock_slowpath() is using spinlock and therefore is a -+ * full barrier. So no need for an arch_lock_acquire_barrier(). -+ */ - } - - static inline void _write_lock_irq(rwlock_t *lock) -@@ -157,11 +179,16 @@ static inline int _write_trylock(rwlock_t *lock) - if ( unlikely(cnts) ) - return 0; - -+ /* -+ * atomic_cmpxchg() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - return likely(atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0); - } - - static inline void _write_unlock(rwlock_t *lock) - { -+ arch_lock_release_barrier(); - /* - * If the writer field is atomic, it can be cleared directly. - * Otherwise, an atomic subtraction will be used to clear it. --- -2.17.1 - diff --git a/xsa316-xen.patch b/xsa316-xen.patch deleted file mode 100644 index 4962b4e..0000000 --- a/xsa316-xen.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: Ross Lagerwall -Subject: xen/gnttab: Fix error path in map_grant_ref() - -Part of XSA-295 (c/s 863e74eb2cffb) inadvertently re-positioned the brackets, -changing the logic. If the _set_status() call fails, the grant_map hypercall -would fail with a status of 1 (rc != GNTST_okay) instead of the expected -negative GNTST_* error. - -This error path can be taken due to bad guest state, and causes net/blk-back -in Linux to crash. - -This is XSA-316. - -Signed-off-by: Ross Lagerwall -Reviewed-by: Andrew Cooper -Reviewed-by: Julien Grall - -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 9fd6e60416..4b5344dc21 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -1031,7 +1031,7 @@ map_grant_ref( - { - if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, - op->flags & GNTMAP_readonly, 1, -- ld->domain_id) != GNTST_okay) ) -+ ld->domain_id)) != GNTST_okay ) - goto act_release_out; - - if ( !act->pin ) diff --git a/xsa318.patch b/xsa318.patch deleted file mode 100644 index f4becdf..0000000 --- a/xsa318.patch +++ /dev/null @@ -1,39 +0,0 @@ -From: Jan Beulich -Subject: gnttab: fix GNTTABOP_copy continuation handling - -The XSA-226 fix was flawed - the backwards transformation on rc was done -too early, causing a continuation to not get invoked when the need for -preemption was determined at the very first iteration of the request. -This in particular means that all of the status fields of the individual -operations would be left untouched, i.e. set to whatever the caller may -or may not have initialized them to. - -This is part of XSA-318. - -Reported-by: Pawel Wieczorkiewicz -Tested-by: Pawel Wieczorkiewicz -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -3576,8 +3576,7 @@ do_grant_table_op( - rc = gnttab_copy(copy, count); - if ( rc > 0 ) - { -- rc = count - rc; -- guest_handle_add_offset(copy, rc); -+ guest_handle_add_offset(copy, count - rc); - uop = guest_handle_cast(copy, void); - } - break; -@@ -3644,6 +3643,9 @@ do_grant_table_op( - out: - if ( rc > 0 || opaque_out != 0 ) - { -+ /* Adjust rc, see gnttab_copy() for why this is needed. */ -+ if ( cmd == GNTTABOP_copy ) -+ rc = count - rc; - ASSERT(rc < count); - ASSERT((opaque_out & GNTTABOP_CMD_MASK) == 0); - rc = hypercall_create_continuation(__HYPERVISOR_grant_table_op, "ihi", From 7a127d3fbe95d96ea44bfcf7fe8c36b932cac78c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 10 Jun 2020 21:20:12 +0100 Subject: [PATCH 05/11] Special Register Buffer speculative side channel [XSA-320] --- xen.spec | 9 ++- xsa320-4.12-1.patch | 133 ++++++++++++++++++++++++++++++++ xsa320-4.12-2.patch | 179 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 xsa320-4.12-1.patch create mode 100644 xsa320-4.12-2.patch diff --git a/xen.spec b/xen.spec index d79e21c..c4fc1e8 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -115,6 +115,8 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch Patch54: xen.python38.patch +Patch55: xsa320-4.12-1.patch +Patch56: xsa320-4.12-2.patch %if %build_qemutrad @@ -313,6 +315,8 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch54 -p1 +%patch55 -p1 +%patch56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -876,6 +880,9 @@ fi %endif %changelog +* Wed Jun 10 2020 Michael Young - 4.12.3-2 +- Special Register Buffer speculative side channel [XSA-320] + * Tue May 19 2020 Michael Young - 4.12.3-1 - update to 4.12.3 remove patches for issues now fixed upstream diff --git a/xsa320-4.12-1.patch b/xsa320-4.12-1.patch new file mode 100644 index 0000000..e1fc277 --- /dev/null +++ b/xsa320-4.12-1.patch @@ -0,0 +1,133 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: CPUID/MSR definitions for Special Register Buffer Data Sampling + +This is part of XSA-320 / CVE-2020-0543 + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Acked-by: Wei Liu + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 3561d88b59..dbdaee92dc 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -483,10 +483,10 @@ accounting for hardware capabilities as enumerated via CPUID. + + Currently accepted: + +-The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, +-`l1d-flush` and `ssbd` are used by default if available and applicable. They can +-be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and +-won't offer them to guests. ++The Speculation Control hardware features `srbds-ctrl`, `md-clear`, `ibrsb`, ++`stibp`, `ibpb`, `l1d-flush` and `ssbd` are used by default if available and ++applicable. They can be ignored, e.g. `no-ibrsb`, at which point Xen won't ++use them itself, and won't offer them to guests. + + ### cpuid_mask_cpu + > `= fam_0f_rev_[cdefg] | fam_10_rev_[bc] | fam_11_rev_b` +diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c +index 4cf0f0738d..88b5760c85 100644 +--- a/tools/libxl/libxl_cpuid.c ++++ b/tools/libxl/libxl_cpuid.c +@@ -203,6 +203,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) + + {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, + {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, ++ {"srbds-ctrl", 0x00000007, 0, CPUID_REG_EDX, 9, 1}, + {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, + {"cet-ibt", 0x00000007, 0, CPUID_REG_EDX, 20, 1}, + {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index 2a00697643..b4c4dfcf19 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -154,6 +154,7 @@ static const char *str_7d0[32] = + [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", + [ 4] = "fsrm", + ++ /* 8 */ [ 9] = "srbds-ctrl", + [10] = "md-clear", + /* 12 */ [13] = "tsx-force-abort", + +diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c +index 1727497459..22d8c71a95 100644 +--- a/xen/arch/x86/cpuid.c ++++ b/xen/arch/x86/cpuid.c +@@ -59,6 +59,11 @@ static int __init parse_xen_cpuid(const char *s) + if ( !val ) + setup_clear_cpu_cap(X86_FEATURE_SSBD); + } ++ else if ( (val = parse_boolean("srbds-ctrl", s, ss)) >= 0 ) ++ { ++ if ( !val ) ++ setup_clear_cpu_cap(X86_FEATURE_SRBDS_CTRL); ++ } + else + rc = -EINVAL; + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 4888fff16c..9ff27b7007 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -133,6 +133,7 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) + /* Write-only */ + case MSR_TSX_FORCE_ABORT: + case MSR_TSX_CTRL: ++ case MSR_MCU_OPT_CTRL: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: +@@ -273,6 +274,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + /* Read-only */ + case MSR_TSX_FORCE_ABORT: + case MSR_TSX_CTRL: ++ case MSR_MCU_OPT_CTRL: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 800139d79c..5158e012ca 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -309,12 +309,13 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + printk("Speculative mitigation facilities:\n"); + + /* Hardware features which pertain to speculative mitigations. */ +- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", ++ (_7d0 & cpufeat_mask(X86_FEATURE_SRBDS_CTRL)) ? " SRBDS_CTRL" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 7693c4a71a..91994669e1 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -179,6 +179,9 @@ + #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x490 + #define MSR_IA32_VMX_VMFUNC 0x491 + ++#define MSR_MCU_OPT_CTRL 0x00000123 ++#define MCU_OPT_CTRL_RNGDS_MITG_DIS (_AC(1, ULL) << 0) ++ + #define MSR_U_CET 0x000006a0 + #define MSR_S_CET 0x000006a2 + #define MSR_PL0_SSP 0x000006a4 +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 865a435d2c..31490a7c10 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -243,6 +243,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ + XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ ++XEN_CPUFEATURE(SRBDS_CTRL, 9*32+ 9) /* MSR_MCU_OPT_CTRL and RNGDS_MITG_DIS. */ + XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ + XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(CET_IBT, 9*32+20) /* CET - Indirect Branch Tracking */ diff --git a/xsa320-4.12-2.patch b/xsa320-4.12-2.patch new file mode 100644 index 0000000..3046f82 --- /dev/null +++ b/xsa320-4.12-2.patch @@ -0,0 +1,179 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate the Special Register Buffer Data Sampling sidechannel + +See patch documentation and comments. + +This is part of XSA-320 / CVE-2020-0543 + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index dbdaee92dc..337fbf0492 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -1909,7 +1909,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + ### spec-ctrl (x86) + > `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, +-> l1d-flush}= ]` ++> l1d-flush,srb-lock}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -1981,6 +1981,12 @@ Irrespective of Xen's setting, the feature is virtualised for HVM guests to + use. By default, Xen will enable this mitigation on hardware believed to be + vulnerable to L1TF. + ++On hardware supporting SRBDS_CTRL, the `srb-lock=` option can be used to force ++or prevent Xen from protect the Special Register Buffer from leaking stale ++data. By default, Xen will enable this mitigation, except on parts where MDS ++is fixed and TAA is fixed/mitigated (in which case, there is believed to be no ++way for an attacker to obtain the stale data). ++ + ### sync_console + > `= ` + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index c1d772f63f..a07aa3b9ed 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -266,6 +266,9 @@ static int enter_state(u32 state) + ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); + spec_ctrl_exit_idle(ci); + ++ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) ++ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); ++ + done: + spin_debug_enable(); + local_irq_restore(flags); +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 699e21bfb7..b741d1354a 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -369,12 +369,14 @@ void start_secondary(void *unused) + microcode_resume_cpu(cpu); + + /* +- * If MSR_SPEC_CTRL is available, apply Xen's default setting and discard +- * any firmware settings. Note: MSR_SPEC_CTRL may only become available +- * after loading microcode. ++ * If any speculative control MSRs are available, apply Xen's default ++ * settings. Note: These MSRs may only become available after loading ++ * microcode. + */ + if ( boot_cpu_has(X86_FEATURE_IBRSB) ) + wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); ++ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) ++ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); + + tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 5158e012ca..e2fcefc86a 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -64,6 +64,9 @@ static unsigned int __initdata l1d_maxphysaddr; + static bool __initdata cpu_has_bug_msbds_only; /* => minimal HT impact. */ + static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. */ + ++static int8_t __initdata opt_srb_lock = -1; ++uint64_t __read_mostly default_xen_mcu_opt_ctrl; ++ + static int __init parse_spec_ctrl(const char *s) + { + const char *ss; +@@ -110,6 +113,7 @@ static int __init parse_spec_ctrl(const char *s) + opt_ibpb = false; + opt_ssbd = false; + opt_l1d_flush = 0; ++ opt_srb_lock = 0; + } + else if ( val > 0 ) + rc = -EINVAL; +@@ -175,6 +179,8 @@ static int __init parse_spec_ctrl(const char *s) + opt_eager_fpu = val; + else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) + opt_l1d_flush = val; ++ else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) ++ opt_srb_lock = val; + else + rc = -EINVAL; + +@@ -338,7 +344,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -349,6 +355,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", + !(caps & ARCH_CAPS_TSX_CTRL) ? "" : + (opt_tsx & 1) ? " TSX+" : " TSX-", ++ !boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ? "" : ++ opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", + opt_ibpb ? " IBPB" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", + opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); +@@ -1142,6 +1150,34 @@ void __init init_speculation_mitigations(void) + tsx_init(); + } + ++ /* Calculate suitable defaults for MSR_MCU_OPT_CTRL */ ++ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) ++ { ++ uint64_t val; ++ ++ rdmsrl(MSR_MCU_OPT_CTRL, val); ++ ++ /* ++ * On some SRBDS-affected hardware, it may be safe to relax srb-lock ++ * by default. ++ * ++ * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only way ++ * to access the Fill Buffer. If TSX isn't available (inc. SKU ++ * reasons on some models), or TSX is explicitly disabled, then there ++ * is no need for the extra overhead to protect RDRAND/RDSEED. ++ */ ++ if ( opt_srb_lock == -1 && ++ (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && ++ (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && opt_tsx == 0)) ) ++ opt_srb_lock = 0; ++ ++ val &= ~MCU_OPT_CTRL_RNGDS_MITG_DIS; ++ if ( !opt_srb_lock ) ++ val |= MCU_OPT_CTRL_RNGDS_MITG_DIS; ++ ++ default_xen_mcu_opt_ctrl = val; ++ } ++ + print_details(thunk, caps); + + /* +@@ -1173,6 +1209,9 @@ void __init init_speculation_mitigations(void) + + wrmsrl(MSR_SPEC_CTRL, bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl); + } ++ ++ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) ++ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); + } + + static void __init __maybe_unused build_assertions(void) +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index ba03bb42e5..59bab1a41b 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -53,6 +53,8 @@ extern int8_t opt_pv_l1tf_hwdom, opt_pv_l1tf_domu; + */ + extern paddr_t l1tf_addr_mask, l1tf_safe_maddr; + ++extern uint64_t default_xen_mcu_opt_ctrl; ++ + static inline void init_shadow_spec_ctrl_state(void) + { + struct cpu_info *info = get_cpu_info(); From 9cb1314e73f5ee51dfeaf417ce509ce073ad755a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 7 Jul 2020 20:47:58 +0100 Subject: [PATCH 06/11] 5 security updates incorrect error handling in event channel port allocation leads to DoS [XSA-317, CVE-2020-15566] (#1854465) inverted code paths in x86 dirty VRAM tracking leads to DoS [XSA-319, CVE-2020-15563] (#1854463) xen: insufficient cache write-back under VT-d leads to DoS [XSA-321, CVE-2020-15565] (#1854467) missing alignment check in VCPUOP_register_vcpu_info leads to DoS [XSA-327, CVE-2020-15564] (#1854458) non-atomic modification of live EPT PTE leads to DoS [XSA-328, CVE-2020-15567] (#1854464) --- xen.spec | 38 +++++++++- xsa317.patch | 50 +++++++++++++ xsa319.patch | 27 +++++++ xsa321-4.12-1.patch | 31 ++++++++ xsa321-4.12-2.patch | 175 ++++++++++++++++++++++++++++++++++++++++++++ xsa321-4.12-3.patch | 82 +++++++++++++++++++++ xsa321-4.12-4.patch | 36 +++++++++ xsa321-4.12-5.patch | 24 ++++++ xsa321-4.12-6.patch | 91 +++++++++++++++++++++++ xsa321-4.12-7.patch | 151 ++++++++++++++++++++++++++++++++++++++ xsa327.patch | 63 ++++++++++++++++ xsa328-1.patch | 126 +++++++++++++++++++++++++++++++ xsa328-2.patch | 56 ++++++++++++++ xsa328-4.12-1.patch | 118 +++++++++++++++++++++++++++++ xsa328-4.12-2.patch | 48 ++++++++++++ xsa328-4.13-1.patch | 118 +++++++++++++++++++++++++++++ xsa328-4.13-2.patch | 48 ++++++++++++ 17 files changed, 1281 insertions(+), 1 deletion(-) create mode 100644 xsa317.patch create mode 100644 xsa319.patch create mode 100644 xsa321-4.12-1.patch create mode 100644 xsa321-4.12-2.patch create mode 100644 xsa321-4.12-3.patch create mode 100644 xsa321-4.12-4.patch create mode 100644 xsa321-4.12-5.patch create mode 100644 xsa321-4.12-6.patch create mode 100644 xsa321-4.12-7.patch create mode 100644 xsa327.patch create mode 100644 xsa328-1.patch create mode 100644 xsa328-2.patch create mode 100644 xsa328-4.12-1.patch create mode 100644 xsa328-4.12-2.patch create mode 100644 xsa328-4.13-1.patch create mode 100644 xsa328-4.13-2.patch diff --git a/xen.spec b/xen.spec index c4fc1e8..1dbf71a 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -117,6 +117,18 @@ Patch43: xen.python3.patch Patch54: xen.python38.patch Patch55: xsa320-4.12-1.patch Patch56: xsa320-4.12-2.patch +Patch57: xsa317.patch +Patch58: xsa319.patch +Patch59: xsa328-4.12-1.patch +Patch60: xsa328-4.12-2.patch +Patch61: xsa321-4.12-1.patch +Patch62: xsa321-4.12-2.patch +Patch63: xsa321-4.12-3.patch +Patch64: xsa321-4.12-4.patch +Patch65: xsa321-4.12-5.patch +Patch66: xsa321-4.12-6.patch +Patch67: xsa321-4.12-7.patch +Patch68: xsa327.patch %if %build_qemutrad @@ -317,6 +329,18 @@ manage Xen virtual machines. %patch54 -p1 %patch55 -p1 %patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -880,6 +904,18 @@ fi %endif %changelog +* Tue Jul 07 2020 Michael Young - 4.12.3-3 +- incorrect error handling in event channel port allocation leads to + DoS [XSA-317, CVE-2020-15566] (#1854465) +- inverted code paths in x86 dirty VRAM tracking leads to DoS + [XSA-319, CVE-2020-15563] (#1854463) +- xen: insufficient cache write-back under VT-d leads to DoS + [XSA-321, CVE-2020-15565] (#1854467) +- missing alignment check in VCPUOP_register_vcpu_info leads to DoS + [XSA-327, CVE-2020-15564] (#1854458) +- non-atomic modification of live EPT PTE leads to DoS + [XSA-328, CVE-2020-15567] (#1854464) + * Wed Jun 10 2020 Michael Young - 4.12.3-2 - Special Register Buffer speculative side channel [XSA-320] diff --git a/xsa317.patch b/xsa317.patch new file mode 100644 index 0000000..20e2c64 --- /dev/null +++ b/xsa317.patch @@ -0,0 +1,50 @@ +From aeb46e92f915f19a61d5a8a1f4b696793f64e6fb Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Thu, 19 Mar 2020 13:17:31 +0000 +Subject: [PATCH] xen/common: event_channel: Don't ignore error in + get_free_port() + +Currently, get_free_port() is assuming that the port has been allocated +when evtchn_allocate_port() is not return -EBUSY. + +However, the function may return an error when: + - We exhausted all the event channels. This can happen if the limit + configured by the administrator for the guest ('max_event_channels' + in xl cfg) is higher than the ABI used by the guest. For instance, + if the guest is using 2L, the limit should not be higher than 4095. + - We cannot allocate memory (e.g Xen has not more memory). + +Users of get_free_port() (such as EVTCHNOP_alloc_unbound) will validly +assuming the port was valid and will next call evtchn_from_port(). This +will result to a crash as the memory backing the event channel structure +is not present. + +Fixes: 368ae9a05fe ("xen/pvshim: forward evtchn ops between L0 Xen and L2 DomU") +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +--- + xen/common/event_channel.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c +index e86e2bfab0..a8d182b584 100644 +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -195,10 +195,10 @@ static int get_free_port(struct domain *d) + { + int rc = evtchn_allocate_port(d, port); + +- if ( rc == -EBUSY ) +- continue; +- +- return port; ++ if ( rc == 0 ) ++ return port; ++ else if ( rc != -EBUSY ) ++ return rc; + } + + return -ENOSPC; +-- +2.17.1 + diff --git a/xsa319.patch b/xsa319.patch new file mode 100644 index 0000000..769443c --- /dev/null +++ b/xsa319.patch @@ -0,0 +1,27 @@ +From: Jan Beulich +Subject: x86/shadow: correct an inverted conditional in dirty VRAM tracking + +This originally was "mfn_x(mfn) == INVALID_MFN". Make it like this +again, taking the opportunity to also drop the unnecessary nearby +braces. + +This is XSA-319. + +Fixes: 246a5a3377c2 ("xen: Use a typesafe to define INVALID_MFN") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -3252,10 +3252,8 @@ int shadow_track_dirty_vram(struct domai + int dirty = 0; + paddr_t sl1ma = dirty_vram->sl1ma[i]; + +- if ( !mfn_eq(mfn, INVALID_MFN) ) +- { ++ if ( mfn_eq(mfn, INVALID_MFN) ) + dirty = 1; +- } + else + { + page = mfn_to_page(mfn); diff --git a/xsa321-4.12-1.patch b/xsa321-4.12-1.patch new file mode 100644 index 0000000..b6c55a7 --- /dev/null +++ b/xsa321-4.12-1.patch @@ -0,0 +1,31 @@ +From: Jan Beulich +Subject: vtd: improve IOMMU TLB flush + +Do not limit PSI flushes to order 0 pages, in order to avoid doing a +full TLB flush if the passed in page has an order greater than 0 and +is aligned. Should increase the performance of IOMMU TLB flushes when +dealing with page orders greater than 0. + +This is part of XSA-321. + +Signed-off-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -611,13 +611,14 @@ static int __must_check iommu_flush_iotl + if ( iommu_domid == -1 ) + continue; + +- if ( page_count != 1 || dfn_eq(dfn, INVALID_DFN) ) ++ if ( !page_count || (page_count & (page_count - 1)) || ++ dfn_eq(dfn, INVALID_DFN) || !IS_ALIGNED(dfn_x(dfn), page_count) ) + rc = iommu_flush_iotlb_dsi(iommu, iommu_domid, + 0, flush_dev_iotlb); + else + rc = iommu_flush_iotlb_psi(iommu, iommu_domid, + dfn_to_daddr(dfn), +- PAGE_ORDER_4K, ++ get_order_from_pages(page_count), + !dma_old_pte_present, + flush_dev_iotlb); + diff --git a/xsa321-4.12-2.patch b/xsa321-4.12-2.patch new file mode 100644 index 0000000..a05205c --- /dev/null +++ b/xsa321-4.12-2.patch @@ -0,0 +1,175 @@ +From: +Subject: vtd: prune (and rename) cache flush functions + +Rename __iommu_flush_cache to iommu_sync_cache and remove +iommu_flush_cache_page. Also remove the iommu_flush_cache_entry +wrapper and just use iommu_sync_cache instead. Note the _entry suffix +was meaningless as the wrapper was already taking a size parameter in +bytes. While there also constify the addr parameter. + +No functional change intended. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -38,8 +38,7 @@ void disable_qinval(struct iommu *iommu) + int enable_intremap(struct iommu *iommu, int eim); + void disable_intremap(struct iommu *iommu); + +-void iommu_flush_cache_entry(void *addr, unsigned int size); +-void iommu_flush_cache_page(void *addr, unsigned long npages); ++void iommu_sync_cache(const void *addr, unsigned int size); + int iommu_alloc(struct acpi_drhd_unit *drhd); + void iommu_free(struct acpi_drhd_unit *drhd); + +--- a/xen/drivers/passthrough/vtd/intremap.c ++++ b/xen/drivers/passthrough/vtd/intremap.c +@@ -231,7 +231,7 @@ static void free_remap_entry(struct iomm + iremap_entries, iremap_entry); + + update_irte(iommu, iremap_entry, &new_ire, false); +- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); ++ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + + unmap_vtd_domain_page(iremap_entries); +@@ -403,7 +403,7 @@ static int ioapic_rte_to_remap_entry(str + } + + update_irte(iommu, iremap_entry, &new_ire, !init); +- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); ++ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + + unmap_vtd_domain_page(iremap_entries); +@@ -694,7 +694,7 @@ static int msi_msg_to_remap_entry( + update_irte(iommu, iremap_entry, &new_ire, msi_desc->irte_initialized); + msi_desc->irte_initialized = true; + +- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); ++ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + + unmap_vtd_domain_page(iremap_entries); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -158,7 +158,8 @@ static void __init free_intel_iommu(stru + } + + static int iommus_incoherent; +-static void __iommu_flush_cache(void *addr, unsigned int size) ++ ++void iommu_sync_cache(const void *addr, unsigned int size) + { + int i; + static unsigned int clflush_size = 0; +@@ -173,16 +174,6 @@ static void __iommu_flush_cache(void *ad + cacheline_flush((char *)addr + i); + } + +-void iommu_flush_cache_entry(void *addr, unsigned int size) +-{ +- __iommu_flush_cache(addr, size); +-} +- +-void iommu_flush_cache_page(void *addr, unsigned long npages) +-{ +- __iommu_flush_cache(addr, PAGE_SIZE * npages); +-} +- + /* Allocate page table, return its machine address */ + u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages) + { +@@ -207,7 +198,7 @@ u64 alloc_pgtable_maddr(struct acpi_drhd + vaddr = __map_domain_page(cur_pg); + memset(vaddr, 0, PAGE_SIZE); + +- iommu_flush_cache_page(vaddr, 1); ++ iommu_sync_cache(vaddr, PAGE_SIZE); + unmap_domain_page(vaddr); + cur_pg++; + } +@@ -242,7 +233,7 @@ static u64 bus_to_context_maddr(struct i + } + set_root_value(*root, maddr); + set_root_present(*root); +- iommu_flush_cache_entry(root, sizeof(struct root_entry)); ++ iommu_sync_cache(root, sizeof(struct root_entry)); + } + maddr = (u64) get_context_addr(*root); + unmap_vtd_domain_page(root_entries); +@@ -300,7 +291,7 @@ static u64 addr_to_dma_page_maddr(struct + */ + dma_set_pte_readable(*pte); + dma_set_pte_writable(*pte); +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + } + + if ( level == 2 ) +@@ -681,7 +672,7 @@ static int __must_check dma_pte_clear_on + *flush_flags |= IOMMU_FLUSHF_modified; + + spin_unlock(&hd->arch.mapping_lock); +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + + unmap_vtd_domain_page(page); + +@@ -720,7 +711,7 @@ static void iommu_free_page_table(struct + iommu_free_pagetable(dma_pte_addr(*pte), next_level); + + dma_clear_pte(*pte); +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + } + + unmap_vtd_domain_page(pt_vaddr); +@@ -1449,7 +1440,7 @@ int domain_context_mapping_one( + context_set_address_width(*context, agaw); + context_set_fault_enable(*context); + context_set_present(*context); +- iommu_flush_cache_entry(context, sizeof(struct context_entry)); ++ iommu_sync_cache(context, sizeof(struct context_entry)); + spin_unlock(&iommu->lock); + + /* Context entry was previously non-present (with domid 0). */ +@@ -1602,7 +1593,7 @@ int domain_context_unmap_one( + + context_clear_present(*context); + context_clear_entry(*context); +- iommu_flush_cache_entry(context, sizeof(struct context_entry)); ++ iommu_sync_cache(context, sizeof(struct context_entry)); + + iommu_domid= domain_iommu_domid(domain, iommu); + if ( iommu_domid == -1 ) +@@ -1837,7 +1828,7 @@ static int __must_check intel_iommu_map_ + + *pte = new; + +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + spin_unlock(&hd->arch.mapping_lock); + unmap_vtd_domain_page(page); + +@@ -1912,7 +1903,7 @@ int iommu_pte_flush(struct domain *d, ui + int iommu_domid; + int rc = 0; + +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + + for_each_drhd_unit ( drhd ) + { +@@ -2777,7 +2768,7 @@ static int __init intel_iommu_quarantine + dma_set_pte_addr(*pte, maddr); + dma_set_pte_readable(*pte); + } +- iommu_flush_cache_page(parent, 1); ++ iommu_sync_cache(parent, PAGE_SIZE); + + unmap_vtd_domain_page(parent); + parent = map_vtd_domain_page(maddr); diff --git a/xsa321-4.12-3.patch b/xsa321-4.12-3.patch new file mode 100644 index 0000000..6021585 --- /dev/null +++ b/xsa321-4.12-3.patch @@ -0,0 +1,82 @@ +From: +Subject: x86/iommu: introduce a cache sync hook + +The hook is only implemented for VT-d and it uses the already existing +iommu_sync_cache function present in VT-d code. The new hook is +added so that the cache can be flushed by code outside of VT-d when +using shared page tables. + +Note that alloc_pgtable_maddr must use the now locally defined +sync_cache function, because IOMMU ops are not yet setup the first +time the function gets called during IOMMU initialization. + +No functional change intended. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -38,7 +38,6 @@ void disable_qinval(struct iommu *iommu) + int enable_intremap(struct iommu *iommu, int eim); + void disable_intremap(struct iommu *iommu); + +-void iommu_sync_cache(const void *addr, unsigned int size); + int iommu_alloc(struct acpi_drhd_unit *drhd); + void iommu_free(struct acpi_drhd_unit *drhd); + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -159,7 +159,7 @@ static void __init free_intel_iommu(stru + + static int iommus_incoherent; + +-void iommu_sync_cache(const void *addr, unsigned int size) ++static void sync_cache(const void *addr, unsigned int size) + { + int i; + static unsigned int clflush_size = 0; +@@ -198,7 +198,7 @@ u64 alloc_pgtable_maddr(struct acpi_drhd + vaddr = __map_domain_page(cur_pg); + memset(vaddr, 0, PAGE_SIZE); + +- iommu_sync_cache(vaddr, PAGE_SIZE); ++ sync_cache(vaddr, PAGE_SIZE); + unmap_domain_page(vaddr); + cur_pg++; + } +@@ -2813,6 +2813,7 @@ const struct iommu_ops __initconstrel in + .iotlb_flush_all = iommu_flush_iotlb_all, + .get_reserved_device_memory = intel_iommu_get_reserved_device_memory, + .dump_p2m_table = vtd_dump_p2m_table, ++ .sync_cache = sync_cache, + }; + + /* +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -101,6 +101,13 @@ extern bool untrusted_msi; + int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, + const uint8_t gvec); + ++#define iommu_sync_cache(addr, size) ({ \ ++ const struct iommu_ops *ops = iommu_get_ops(); \ ++ \ ++ if ( ops->sync_cache ) \ ++ ops->sync_cache(addr, size); \ ++}) ++ + #endif /* !__ARCH_X86_IOMMU_H__ */ + /* + * Local variables: +--- a/xen/include/xen/iommu.h ++++ b/xen/include/xen/iommu.h +@@ -221,6 +221,7 @@ struct iommu_ops { + void (*update_ire_from_apic)(unsigned int apic, unsigned int reg, unsigned int value); + unsigned int (*read_apic_from_ire)(unsigned int apic, unsigned int reg); + int (*setup_hpet_msi)(struct msi_desc *); ++ void (*sync_cache)(const void *addr, unsigned int size); + #endif /* CONFIG_X86 */ + int __must_check (*suspend)(void); + void (*resume)(void); diff --git a/xsa321-4.12-4.patch b/xsa321-4.12-4.patch new file mode 100644 index 0000000..24cea6d --- /dev/null +++ b/xsa321-4.12-4.patch @@ -0,0 +1,36 @@ +From: +Subject: vtd: don't assume addresses are aligned in sync_cache + +Current code in sync_cache assume that the address passed in is +aligned to a cache line size. Fix the code to support passing in +arbitrary addresses not necessarily aligned to a cache line size. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -161,8 +161,8 @@ static int iommus_incoherent; + + static void sync_cache(const void *addr, unsigned int size) + { +- int i; +- static unsigned int clflush_size = 0; ++ static unsigned long clflush_size = 0; ++ const void *end = addr + size; + + if ( !iommus_incoherent ) + return; +@@ -170,8 +170,9 @@ static void sync_cache(const void *addr, + if ( clflush_size == 0 ) + clflush_size = get_cache_line_size(); + +- for ( i = 0; i < size; i += clflush_size ) +- cacheline_flush((char *)addr + i); ++ addr -= (unsigned long)addr & (clflush_size - 1); ++ for ( ; addr < end; addr += clflush_size ) ++ cacheline_flush((char *)addr); + } + + /* Allocate page table, return its machine address */ diff --git a/xsa321-4.12-5.patch b/xsa321-4.12-5.patch new file mode 100644 index 0000000..9d47529 --- /dev/null +++ b/xsa321-4.12-5.patch @@ -0,0 +1,24 @@ +From: +Subject: x86/alternative: introduce alternative_2 + +It's based on alternative_io_2 without inputs or outputs but with an +added memory clobber. + +This is part of XSA-321. + +Acked-by: Jan Beulich + +--- a/xen/include/asm-x86/alternative.h ++++ b/xen/include/asm-x86/alternative.h +@@ -113,6 +113,11 @@ extern void alternative_instructions(voi + #define alternative(oldinstr, newinstr, feature) \ + asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory") + ++#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ ++ asm volatile (ALTERNATIVE_2(oldinstr, newinstr1, feature1, \ ++ newinstr2, feature2) \ ++ : : : "memory") ++ + /* + * Alternative inline assembly with input. + * diff --git a/xsa321-4.12-6.patch b/xsa321-4.12-6.patch new file mode 100644 index 0000000..a4bae3e --- /dev/null +++ b/xsa321-4.12-6.patch @@ -0,0 +1,91 @@ +From: +Subject: vtd: optimize CPU cache sync + +Some VT-d IOMMUs are non-coherent, which requires a cache write back +in order for the changes made by the CPU to be visible to the IOMMU. +This cache write back was unconditionally done using clflush, but there are +other more efficient instructions to do so, hence implement support +for them using the alternative framework. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -64,7 +64,6 @@ int __must_check qinval_device_iotlb_syn + u16 did, u16 size, u64 addr); + + unsigned int get_cache_line_size(void); +-void cacheline_flush(char *); + void flush_all_cache(void); + + u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -172,7 +173,42 @@ static void sync_cache(const void *addr, + + addr -= (unsigned long)addr & (clflush_size - 1); + for ( ; addr < end; addr += clflush_size ) +- cacheline_flush((char *)addr); ++/* ++ * The arguments to a macro must not include preprocessor directives. Doing so ++ * results in undefined behavior, so we have to create some defines here in ++ * order to avoid it. ++ */ ++#if defined(HAVE_AS_CLWB) ++# define CLWB_ENCODING "clwb %[p]" ++#elif defined(HAVE_AS_XSAVEOPT) ++# define CLWB_ENCODING "data16 xsaveopt %[p]" /* clwb */ ++#else ++# define CLWB_ENCODING ".byte 0x66, 0x0f, 0xae, 0x30" /* clwb (%%rax) */ ++#endif ++ ++#define BASE_INPUT(addr) [p] "m" (*(const char *)(addr)) ++#if defined(HAVE_AS_CLWB) || defined(HAVE_AS_XSAVEOPT) ++# define INPUT BASE_INPUT ++#else ++# define INPUT(addr) "a" (addr), BASE_INPUT(addr) ++#endif ++ /* ++ * Note regarding the use of NOP_DS_PREFIX: it's faster to do a clflush ++ * + prefix than a clflush + nop, and hence the prefix is added instead ++ * of letting the alternative framework fill the gap by appending nops. ++ */ ++ alternative_io_2(".byte " __stringify(NOP_DS_PREFIX) "; clflush %[p]", ++ "data16 clflush %[p]", /* clflushopt */ ++ X86_FEATURE_CLFLUSHOPT, ++ CLWB_ENCODING, ++ X86_FEATURE_CLWB, /* no outputs */, ++ INPUT(addr)); ++#undef INPUT ++#undef BASE_INPUT ++#undef CLWB_ENCODING ++ ++ alternative_2("", "sfence", X86_FEATURE_CLFLUSHOPT, ++ "sfence", X86_FEATURE_CLWB); + } + + /* Allocate page table, return its machine address */ +--- a/xen/drivers/passthrough/vtd/x86/vtd.c ++++ b/xen/drivers/passthrough/vtd/x86/vtd.c +@@ -51,11 +51,6 @@ unsigned int get_cache_line_size(void) + return ((cpuid_ebx(1) >> 8) & 0xff) * 8; + } + +-void cacheline_flush(char * addr) +-{ +- clflush(addr); +-} +- + void flush_all_cache() + { + wbinvd(); diff --git a/xsa321-4.12-7.patch b/xsa321-4.12-7.patch new file mode 100644 index 0000000..7b9feb8 --- /dev/null +++ b/xsa321-4.12-7.patch @@ -0,0 +1,151 @@ +From: +Subject: x86/ept: flush cache when modifying PTEs and sharing page tables + +Modifications made to the page tables by EPT code need to be written +to memory when the page tables are shared with the IOMMU, as Intel +IOMMUs can be non-coherent and thus require changes to be written to +memory in order to be visible to the IOMMU. + +In order to achieve this make sure data is written back to memory +after writing an EPT entry when the recalc bit is not set in +atomic_write_ept_entry. If such bit is set, the entry will be +adjusted and atomic_write_ept_entry will be called a second time +without the recalc bit set. Note that when splitting a super page the +new tables resulting of the split should also be written back. + +Failure to do so can allow devices behind the IOMMU access to the +stale super page, or cause coherency issues as changes made by the +processor to the page tables are not visible to the IOMMU. + +This allows to remove the VT-d specific iommu_pte_flush helper, since +the cache write back is now performed by atomic_write_ept_entry, and +hence iommu_iotlb_flush can be used to flush the IOMMU TLB. The newly +used method (iommu_iotlb_flush) can result in less flushes, since it +might sometimes be called rightly with 0 flags, in which case it +becomes a no-op. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -58,6 +58,19 @@ static int atomic_write_ept_entry(struct + + write_atomic(&entryptr->epte, new.epte); + ++ /* ++ * The recalc field on the EPT is used to signal either that a ++ * recalculation of the EMT field is required (which doesn't effect the ++ * IOMMU), or a type change. Type changes can only be between ram_rw, ++ * logdirty and ioreq_server: changes to/from logdirty won't work well with ++ * an IOMMU anyway, as IOMMU #PFs are not synchronous and will lead to ++ * aborts, and changes to/from ioreq_server are already fully flushed ++ * before returning to guest context (see ++ * XEN_DMOP_map_mem_type_to_ioreq_server). ++ */ ++ if ( !new.recalc && iommu_use_hap_pt(p2m->domain) ) ++ iommu_sync_cache(entryptr, sizeof(*entryptr)); ++ + return 0; + } + +@@ -278,6 +291,9 @@ static bool_t ept_split_super_page(struc + break; + } + ++ if ( iommu_use_hap_pt(p2m->domain) ) ++ iommu_sync_cache(table, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); ++ + unmap_domain_page(table); + + /* Even failed we should install the newly allocated ept page. */ +@@ -337,6 +353,9 @@ static int ept_next_level(struct p2m_dom + if ( !next ) + return GUEST_TABLE_MAP_FAILED; + ++ if ( iommu_use_hap_pt(p2m->domain) ) ++ iommu_sync_cache(next, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); ++ + rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); + ASSERT(rc == 0); + } +@@ -815,7 +834,10 @@ out: + need_modify_vtd_table ) + { + if ( iommu_use_hap_pt(d) ) +- rc = iommu_pte_flush(d, gfn, &ept_entry->epte, order, vtd_pte_present); ++ rc = iommu_iotlb_flush(d, _dfn(gfn), (1u << order), ++ (iommu_flags ? IOMMU_FLUSHF_added : 0) | ++ (vtd_pte_present ? IOMMU_FLUSHF_modified ++ : 0)); + else if ( need_iommu_pt_sync(d) ) + rc = iommu_flags ? + iommu_legacy_map(d, _dfn(gfn), mfn, order, iommu_flags) : +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1930,53 +1930,6 @@ static int intel_iommu_lookup_page(struc + return 0; + } + +-int iommu_pte_flush(struct domain *d, uint64_t dfn, uint64_t *pte, +- int order, int present) +-{ +- struct acpi_drhd_unit *drhd; +- struct iommu *iommu = NULL; +- struct domain_iommu *hd = dom_iommu(d); +- bool_t flush_dev_iotlb; +- int iommu_domid; +- int rc = 0; +- +- iommu_sync_cache(pte, sizeof(struct dma_pte)); +- +- for_each_drhd_unit ( drhd ) +- { +- iommu = drhd->iommu; +- if ( !test_bit(iommu->index, &hd->arch.iommu_bitmap) ) +- continue; +- +- flush_dev_iotlb = !!find_ats_dev_drhd(iommu); +- iommu_domid= domain_iommu_domid(d, iommu); +- if ( iommu_domid == -1 ) +- continue; +- +- rc = iommu_flush_iotlb_psi(iommu, iommu_domid, +- __dfn_to_daddr(dfn), +- order, !present, flush_dev_iotlb); +- if ( rc > 0 ) +- { +- iommu_flush_write_buffer(iommu); +- rc = 0; +- } +- } +- +- if ( unlikely(rc) ) +- { +- if ( !d->is_shutting_down && printk_ratelimit() ) +- printk(XENLOG_ERR VTDPREFIX +- " d%d: IOMMU pages flush failed: %d\n", +- d->domain_id, rc); +- +- if ( !is_hardware_domain(d) ) +- domain_crash(d); +- } +- +- return rc; +-} +- + static int __init vtd_ept_page_compatible(struct iommu *iommu) + { + u64 ept_cap, vtd_cap = iommu->cap; +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -90,8 +90,6 @@ int iommu_setup_hpet_msi(struct msi_desc + + /* While VT-d specific, this must get declared in a generic header. */ + int adjust_vtd_irq_affinities(void); +-int __must_check iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, +- int order, int present); + bool_t iommu_supports_eim(void); + int iommu_enable_x2apic_IR(void); + void iommu_disable_x2apic_IR(void); diff --git a/xsa327.patch b/xsa327.patch new file mode 100644 index 0000000..0541cfa --- /dev/null +++ b/xsa327.patch @@ -0,0 +1,63 @@ +From 030300ebbb86c40c12db038714479d746167c767 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 26 May 2020 18:31:33 +0100 +Subject: [PATCH] xen: Check the alignment of the offset pased via + VCPUOP_register_vcpu_info + +Currently a guest is able to register any guest physical address to use +for the vcpu_info structure as long as the structure can fits in the +rest of the frame. + +This means a guest can provide an address that is not aligned to the +natural alignment of the structure. + +On Arm 32-bit, unaligned access are completely forbidden by the +hypervisor. This will result to a data abort which is fatal. + +On Arm 64-bit, unaligned access are only forbidden when used for atomic +access. As the structure contains fields (such as evtchn_pending_self) +that are updated using atomic operations, any unaligned access will be +fatal as well. + +While the misalignment is only fatal on Arm, a generic check is added +as an x86 guest shouldn't sensibly pass an unaligned address (this +would result to a split lock). + +This is XSA-327. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Andrew Cooper +Reviewed-by: Stefano Stabellini +--- + xen/common/domain.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 7cc9526139a6..e9be05f1d05f 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1227,10 +1227,20 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + void *mapping; + vcpu_info_t *new_info; + struct page_info *page; ++ unsigned int align; + + if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) + return -EINVAL; + ++#ifdef CONFIG_COMPAT ++ if ( has_32bit_shinfo(d) ) ++ align = alignof(new_info->compat); ++ else ++#endif ++ align = alignof(*new_info); ++ if ( offset & (align - 1) ) ++ return -EINVAL; ++ + if ( !mfn_eq(v->vcpu_info_mfn, INVALID_MFN) ) + return -EINVAL; + +-- +2.17.1 + diff --git a/xsa328-1.patch b/xsa328-1.patch new file mode 100644 index 0000000..eb4df59 --- /dev/null +++ b/xsa328-1.patch @@ -0,0 +1,126 @@ +From: Jan Beulich +Subject: x86/EPT: ept_set_middle_entry() related adjustments + +ept_split_super_page() wants to further modify the newly allocated +table, so have ept_set_middle_entry() return the mapped pointer rather +than tearing it down and then getting re-established right again. + +Similarly ept_next_level() wants to hand back a mapped pointer of +the next level page, so re-use the one established by +ept_set_middle_entry() in case that path was taken. + +Pull the setting of suppress_ve ahead of insertion into the higher level +table, and don't have ept_split_super_page() set the field a 2nd time. + +This is part of XSA-328. + +Signed-off-by: Jan Beulich +--- + xen/arch/x86/mm/p2m-ept.c | 41 +++++++++++++++++---------------------- + 1 file changed, 18 insertions(+), 23 deletions(-) + +diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c +index 293f3e9419..d9913a6c97 100644 +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -186,8 +186,9 @@ static void ept_p2m_type_to_flags(const struct p2m_domain *p2m, + #define GUEST_TABLE_SUPER_PAGE 2 + #define GUEST_TABLE_POD_PAGE 3 + +-/* Fill in middle levels of ept table */ +-static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) ++/* Fill in middle level of ept table; return pointer to mapped new table. */ ++static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, ++ ept_entry_t *ept_entry) + { + mfn_t mfn; + ept_entry_t *table; +@@ -195,7 +196,12 @@ static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) + + mfn = p2m_alloc_ptp(p2m, 0); + if ( mfn_eq(mfn, INVALID_MFN) ) +- return 0; ++ return NULL; ++ ++ table = map_domain_page(mfn); ++ ++ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) ++ table[i].suppress_ve = 1; + + ept_entry->epte = 0; + ept_entry->mfn = mfn_x(mfn); +@@ -207,14 +213,7 @@ static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) + + ept_entry->suppress_ve = 1; + +- table = map_domain_page(mfn); +- +- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +- table[i].suppress_ve = 1; +- +- unmap_domain_page(table); +- +- return 1; ++ return table; + } + + /* free ept sub tree behind an entry */ +@@ -252,10 +251,10 @@ static bool_t ept_split_super_page(struct p2m_domain *p2m, + + ASSERT(is_epte_superpage(ept_entry)); + +- if ( !ept_set_middle_entry(p2m, &new_ept) ) ++ table = ept_set_middle_entry(p2m, &new_ept); ++ if ( !table ) + return 0; + +- table = map_domain_page(_mfn(new_ept.mfn)); + trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); + + for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +@@ -266,7 +265,6 @@ static bool_t ept_split_super_page(struct p2m_domain *p2m, + epte->sp = (level > 1); + epte->mfn += i * trunk; + epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; +- epte->suppress_ve = 1; + + ept_p2m_type_to_flags(p2m, epte); + +@@ -305,8 +303,7 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, + ept_entry_t **table, unsigned long *gfn_remainder, + int next_level) + { +- unsigned long mfn; +- ept_entry_t *ept_entry, e; ++ ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + + shift = next_level * EPT_TABLE_ORDER; +@@ -331,19 +328,17 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- if ( !ept_set_middle_entry(p2m, ept_entry) ) ++ next = ept_set_middle_entry(p2m, ept_entry); ++ if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- else +- e = atomic_read_ept_entry(ept_entry); /* Refresh */ ++ /* e is now stale and hence may not be used anymore below. */ + } +- + /* The only time sp would be set here is if we had hit a superpage */ +- if ( is_epte_superpage(&e) ) ++ else if ( is_epte_superpage(&e) ) + return GUEST_TABLE_SUPER_PAGE; + +- mfn = e.mfn; + unmap_domain_page(*table); +- *table = map_domain_page(_mfn(mfn)); ++ *table = next ?: map_domain_page(_mfn(e.mfn)); + *gfn_remainder &= (1UL << shift) - 1; + return GUEST_TABLE_NORMAL_PAGE; + } +-- +2.26.2 + diff --git a/xsa328-2.patch b/xsa328-2.patch new file mode 100644 index 0000000..9614feb --- /dev/null +++ b/xsa328-2.patch @@ -0,0 +1,56 @@ +From: +Subject: x86/ept: atomically modify entries in ept_next_level + +ept_next_level was passing a live PTE pointer to ept_set_middle_entry, +which was then modified without taking into account that the PTE could +be part of a live EPT table. This wasn't a security issue because the +pages returned by p2m_alloc_ptp are zeroed, so adding such an entry +before actually initializing it didn't allow a guest to access +physical memory addresses it wasn't supposed to access. + +This is part of XSA-328. + +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm/p2m-ept.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c +index d9913a6c97..87a14f6f22 100644 +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -306,6 +306,8 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, + ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + ++ ASSERT(next_level); ++ + shift = next_level * EPT_TABLE_ORDER; + + index = *gfn_remainder >> shift; +@@ -322,16 +324,20 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, + + if ( !is_epte_present(&e) ) + { ++ int rc; ++ + if ( e.sa_p2mt == p2m_populate_on_demand ) + return GUEST_TABLE_POD_PAGE; + + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- next = ept_set_middle_entry(p2m, ept_entry); ++ next = ept_set_middle_entry(p2m, &e); + if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- /* e is now stale and hence may not be used anymore below. */ ++ ++ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); ++ ASSERT(rc == 0); + } + /* The only time sp would be set here is if we had hit a superpage */ + else if ( is_epte_superpage(&e) ) +-- +2.26.2 + diff --git a/xsa328-4.12-1.patch b/xsa328-4.12-1.patch new file mode 100644 index 0000000..a53993c --- /dev/null +++ b/xsa328-4.12-1.patch @@ -0,0 +1,118 @@ +From: Jan Beulich +Subject: x86/EPT: ept_set_middle_entry() related adjustments + +ept_split_super_page() wants to further modify the newly allocated +table, so have ept_set_middle_entry() return the mapped pointer rather +than tearing it down and then getting re-established right again. + +Similarly ept_next_level() wants to hand back a mapped pointer of +the next level page, so re-use the one established by +ept_set_middle_entry() in case that path was taken. + +Pull the setting of suppress_ve ahead of insertion into the higher level +table, and don't have ept_split_super_page() set the field a 2nd time. + +This is part of XSA-328. + +Signed-off-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -187,8 +187,9 @@ static void ept_p2m_type_to_flags(struct + #define GUEST_TABLE_SUPER_PAGE 2 + #define GUEST_TABLE_POD_PAGE 3 + +-/* Fill in middle levels of ept table */ +-static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) ++/* Fill in middle level of ept table; return pointer to mapped new table. */ ++static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, ++ ept_entry_t *ept_entry) + { + mfn_t mfn; + ept_entry_t *table; +@@ -196,7 +197,12 @@ static int ept_set_middle_entry(struct p + + mfn = p2m_alloc_ptp(p2m, 0); + if ( mfn_eq(mfn, INVALID_MFN) ) +- return 0; ++ return NULL; ++ ++ table = map_domain_page(mfn); ++ ++ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) ++ table[i].suppress_ve = 1; + + ept_entry->epte = 0; + ept_entry->mfn = mfn_x(mfn); +@@ -208,14 +214,7 @@ static int ept_set_middle_entry(struct p + + ept_entry->suppress_ve = 1; + +- table = map_domain_page(mfn); +- +- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +- table[i].suppress_ve = 1; +- +- unmap_domain_page(table); +- +- return 1; ++ return table; + } + + /* free ept sub tree behind an entry */ +@@ -253,10 +252,10 @@ static bool_t ept_split_super_page(struc + + ASSERT(is_epte_superpage(ept_entry)); + +- if ( !ept_set_middle_entry(p2m, &new_ept) ) ++ table = ept_set_middle_entry(p2m, &new_ept); ++ if ( !table ) + return 0; + +- table = map_domain_page(_mfn(new_ept.mfn)); + trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); + + for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +@@ -267,7 +266,6 @@ static bool_t ept_split_super_page(struc + epte->sp = (level > 1); + epte->mfn += i * trunk; + epte->snp = (iommu_enabled && iommu_snoop); +- epte->suppress_ve = 1; + + ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access); + +@@ -306,8 +304,7 @@ static int ept_next_level(struct p2m_dom + ept_entry_t **table, unsigned long *gfn_remainder, + int next_level) + { +- unsigned long mfn; +- ept_entry_t *ept_entry, e; ++ ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + + shift = next_level * EPT_TABLE_ORDER; +@@ -332,19 +329,17 @@ static int ept_next_level(struct p2m_dom + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- if ( !ept_set_middle_entry(p2m, ept_entry) ) ++ next = ept_set_middle_entry(p2m, ept_entry); ++ if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- else +- e = atomic_read_ept_entry(ept_entry); /* Refresh */ ++ /* e is now stale and hence may not be used anymore below. */ + } +- + /* The only time sp would be set here is if we had hit a superpage */ +- if ( is_epte_superpage(&e) ) ++ else if ( is_epte_superpage(&e) ) + return GUEST_TABLE_SUPER_PAGE; + +- mfn = e.mfn; + unmap_domain_page(*table); +- *table = map_domain_page(_mfn(mfn)); ++ *table = next ?: map_domain_page(_mfn(e.mfn)); + *gfn_remainder &= (1UL << shift) - 1; + return GUEST_TABLE_NORMAL_PAGE; + } diff --git a/xsa328-4.12-2.patch b/xsa328-4.12-2.patch new file mode 100644 index 0000000..c4f437f --- /dev/null +++ b/xsa328-4.12-2.patch @@ -0,0 +1,48 @@ +From: +Subject: x86/ept: atomically modify entries in ept_next_level + +ept_next_level was passing a live PTE pointer to ept_set_middle_entry, +which was then modified without taking into account that the PTE could +be part of a live EPT table. This wasn't a security issue because the +pages returned by p2m_alloc_ptp are zeroed, so adding such an entry +before actually initializing it didn't allow a guest to access +physical memory addresses it wasn't supposed to access. + +This is part of XSA-328. + +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -307,6 +307,8 @@ static int ept_next_level(struct p2m_dom + ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + ++ ASSERT(next_level); ++ + shift = next_level * EPT_TABLE_ORDER; + + index = *gfn_remainder >> shift; +@@ -323,16 +325,20 @@ static int ept_next_level(struct p2m_dom + + if ( !is_epte_present(&e) ) + { ++ int rc; ++ + if ( e.sa_p2mt == p2m_populate_on_demand ) + return GUEST_TABLE_POD_PAGE; + + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- next = ept_set_middle_entry(p2m, ept_entry); ++ next = ept_set_middle_entry(p2m, &e); + if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- /* e is now stale and hence may not be used anymore below. */ ++ ++ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); ++ ASSERT(rc == 0); + } + /* The only time sp would be set here is if we had hit a superpage */ + else if ( is_epte_superpage(&e) ) diff --git a/xsa328-4.13-1.patch b/xsa328-4.13-1.patch new file mode 100644 index 0000000..56e48de --- /dev/null +++ b/xsa328-4.13-1.patch @@ -0,0 +1,118 @@ +From: Jan Beulich +Subject: x86/EPT: ept_set_middle_entry() related adjustments + +ept_split_super_page() wants to further modify the newly allocated +table, so have ept_set_middle_entry() return the mapped pointer rather +than tearing it down and then getting re-established right again. + +Similarly ept_next_level() wants to hand back a mapped pointer of +the next level page, so re-use the one established by +ept_set_middle_entry() in case that path was taken. + +Pull the setting of suppress_ve ahead of insertion into the higher level +table, and don't have ept_split_super_page() set the field a 2nd time. + +This is part of XSA-328. + +Signed-off-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -187,8 +187,9 @@ static void ept_p2m_type_to_flags(struct + #define GUEST_TABLE_SUPER_PAGE 2 + #define GUEST_TABLE_POD_PAGE 3 + +-/* Fill in middle levels of ept table */ +-static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) ++/* Fill in middle level of ept table; return pointer to mapped new table. */ ++static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, ++ ept_entry_t *ept_entry) + { + mfn_t mfn; + ept_entry_t *table; +@@ -196,7 +197,12 @@ static int ept_set_middle_entry(struct p + + mfn = p2m_alloc_ptp(p2m, 0); + if ( mfn_eq(mfn, INVALID_MFN) ) +- return 0; ++ return NULL; ++ ++ table = map_domain_page(mfn); ++ ++ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) ++ table[i].suppress_ve = 1; + + ept_entry->epte = 0; + ept_entry->mfn = mfn_x(mfn); +@@ -208,14 +214,7 @@ static int ept_set_middle_entry(struct p + + ept_entry->suppress_ve = 1; + +- table = map_domain_page(mfn); +- +- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +- table[i].suppress_ve = 1; +- +- unmap_domain_page(table); +- +- return 1; ++ return table; + } + + /* free ept sub tree behind an entry */ +@@ -253,10 +252,10 @@ static bool_t ept_split_super_page(struc + + ASSERT(is_epte_superpage(ept_entry)); + +- if ( !ept_set_middle_entry(p2m, &new_ept) ) ++ table = ept_set_middle_entry(p2m, &new_ept); ++ if ( !table ) + return 0; + +- table = map_domain_page(_mfn(new_ept.mfn)); + trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); + + for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +@@ -267,7 +266,6 @@ static bool_t ept_split_super_page(struc + epte->sp = (level > 1); + epte->mfn += i * trunk; + epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; +- epte->suppress_ve = 1; + + ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access); + +@@ -306,8 +304,7 @@ static int ept_next_level(struct p2m_dom + ept_entry_t **table, unsigned long *gfn_remainder, + int next_level) + { +- unsigned long mfn; +- ept_entry_t *ept_entry, e; ++ ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + + shift = next_level * EPT_TABLE_ORDER; +@@ -332,19 +329,17 @@ static int ept_next_level(struct p2m_dom + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- if ( !ept_set_middle_entry(p2m, ept_entry) ) ++ next = ept_set_middle_entry(p2m, ept_entry); ++ if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- else +- e = atomic_read_ept_entry(ept_entry); /* Refresh */ ++ /* e is now stale and hence may not be used anymore below. */ + } +- + /* The only time sp would be set here is if we had hit a superpage */ +- if ( is_epte_superpage(&e) ) ++ else if ( is_epte_superpage(&e) ) + return GUEST_TABLE_SUPER_PAGE; + +- mfn = e.mfn; + unmap_domain_page(*table); +- *table = map_domain_page(_mfn(mfn)); ++ *table = next ?: map_domain_page(_mfn(e.mfn)); + *gfn_remainder &= (1UL << shift) - 1; + return GUEST_TABLE_NORMAL_PAGE; + } diff --git a/xsa328-4.13-2.patch b/xsa328-4.13-2.patch new file mode 100644 index 0000000..c4f437f --- /dev/null +++ b/xsa328-4.13-2.patch @@ -0,0 +1,48 @@ +From: +Subject: x86/ept: atomically modify entries in ept_next_level + +ept_next_level was passing a live PTE pointer to ept_set_middle_entry, +which was then modified without taking into account that the PTE could +be part of a live EPT table. This wasn't a security issue because the +pages returned by p2m_alloc_ptp are zeroed, so adding such an entry +before actually initializing it didn't allow a guest to access +physical memory addresses it wasn't supposed to access. + +This is part of XSA-328. + +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -307,6 +307,8 @@ static int ept_next_level(struct p2m_dom + ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + ++ ASSERT(next_level); ++ + shift = next_level * EPT_TABLE_ORDER; + + index = *gfn_remainder >> shift; +@@ -323,16 +325,20 @@ static int ept_next_level(struct p2m_dom + + if ( !is_epte_present(&e) ) + { ++ int rc; ++ + if ( e.sa_p2mt == p2m_populate_on_demand ) + return GUEST_TABLE_POD_PAGE; + + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- next = ept_set_middle_entry(p2m, ept_entry); ++ next = ept_set_middle_entry(p2m, &e); + if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- /* e is now stale and hence may not be used anymore below. */ ++ ++ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); ++ ASSERT(rc == 0); + } + /* The only time sp would be set here is if we had hit a superpage */ + else if ( is_epte_superpage(&e) ) From d1afe2704cba2bb3077784d09eef5a1b41ddcf6a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 25 Aug 2020 15:52:30 +0100 Subject: [PATCH 07/11] QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) --- xen.spec | 10 +++++- xsa335-qemu.patch | 84 +++++++++++++++++++++++++++++++++++++++++++++++ xsa335-trad.patch | 45 +++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 xsa335-qemu.patch create mode 100644 xsa335-trad.patch diff --git a/xen.spec b/xen.spec index 1dbf71a..1fc88db 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -129,6 +129,8 @@ Patch65: xsa321-4.12-5.patch Patch66: xsa321-4.12-6.patch Patch67: xsa321-4.12-7.patch Patch68: xsa327.patch +Patch69: xsa335-qemu.patch +Patch70: xsa335-trad.patch %if %build_qemutrad @@ -341,6 +343,7 @@ manage Xen virtual machines. %patch66 -p1 %patch67 -p1 %patch68 -p1 +%patch70 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -357,6 +360,7 @@ popd # qemu-xen patches pushd tools/qemu-xen +%patch69 -p1 popd # stubdom sources @@ -904,6 +908,10 @@ fi %endif %changelog +* Tue Aug 25 2020 Michael Young - 4.12.3-4 +- QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] + (#1871850) + * Tue Jul 07 2020 Michael Young - 4.12.3-3 - incorrect error handling in event channel port allocation leads to DoS [XSA-317, CVE-2020-15566] (#1854465) diff --git a/xsa335-qemu.patch b/xsa335-qemu.patch new file mode 100644 index 0000000..5617502 --- /dev/null +++ b/xsa335-qemu.patch @@ -0,0 +1,84 @@ +From c5bd2924c6d6a5bcbffb8b5e7798a88970131c07 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 17 Aug 2020 08:34:22 +0200 +Subject: [PATCH] usb: fix setup_len init (CVE-2020-14364) + +Store calculated setup_len in a local variable, verify it, and only +write it to the struct (USBDevice->setup_len) in case it passed the +sanity checks. + +This prevents other code (do_token_{in,out} functions specifically) +from working with invalid USBDevice->setup_len values and overrunning +the USBDevice->setup_buf[] buffer. + +Fixes: CVE-2020-14364 +Signed-off-by: Gerd Hoffmann +--- + hw/usb/core.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index 5abd128b6bc5..5234dcc73fea 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -129,6 +129,7 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) + static void do_token_setup(USBDevice *s, USBPacket *p) + { + int request, value, index; ++ unsigned int setup_len; + + if (p->iov.size != 8) { + p->status = USB_RET_STALL; +@@ -138,14 +139,15 @@ static void do_token_setup(USBDevice *s, USBPacket *p) + usb_packet_copy(p, s->setup_buf, p->iov.size); + s->setup_index = 0; + p->actual_length = 0; +- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; +- if (s->setup_len > sizeof(s->data_buf)) { ++ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; ++ if (setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", +- s->setup_len, sizeof(s->data_buf)); ++ setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } ++ s->setup_len = setup_len; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; +@@ -259,26 +261,28 @@ static void do_token_out(USBDevice *s, USBPacket *p) + static void do_parameter(USBDevice *s, USBPacket *p) + { + int i, request, value, index; ++ unsigned int setup_len; + + for (i = 0; i < 8; i++) { + s->setup_buf[i] = p->parameter >> (i*8); + } + + s->setup_state = SETUP_STATE_PARAM; +- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; + index = (s->setup_buf[5] << 8) | s->setup_buf[4]; + +- if (s->setup_len > sizeof(s->data_buf)) { ++ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; ++ if (setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", +- s->setup_len, sizeof(s->data_buf)); ++ setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } ++ s->setup_len = setup_len; + + if (p->pid == USB_TOKEN_OUT) { + usb_packet_copy(p, s->data_buf, s->setup_len); +-- +2.18.4 diff --git a/xsa335-trad.patch b/xsa335-trad.patch new file mode 100644 index 0000000..1310b84 --- /dev/null +++ b/xsa335-trad.patch @@ -0,0 +1,45 @@ +From a62cdd675bc6a8053f6797b6add29b2853b081e3 Mon Sep 17 00:00:00 2001 +From: Ian Jackson +Date: Wed, 19 Aug 2020 18:31:45 +0100 +Subject: [PATCH] SUPPORT.md: Desupport qemu trad except stub dm + +While investigating XSA-335 we discovered that many upstream security +fixes were missing. It is not practical to backport them. There is +no good reason to be running this very ancient version of qemu, except +that it is the only way to run a stub dm which is currently supported +by upstream. + +Signed-off-by: Ian Jackson +--- + SUPPORT.md | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/SUPPORT.md b/SUPPORT.md +index 1479055c45..b0939052e2 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -758,6 +758,21 @@ See the section **Blkback** for image formats supported by QEMU. + + Status: Supported, not security supported + ++### qemu-xen-traditional ### ++ ++The Xen Project provides an old version of qemu with modifications ++which enable use as a device model stub domain. The old version is ++normally selected by default only in a stub dm configuration, but it ++can be requested explicitly in other configurations, for example in ++`xl` with `device_model_version="QEMU_XEN_TRADITIONAL"`. ++ ++ Status, Device Model Stub Domains: Supported, with caveats ++ Status, as host process device model: No security support, not recommended ++ ++qemu-xen-traditional is security supported only for those available ++devices which are supported for mainstream QEMU (see above), with ++trusted driver domains (see Device Model Stub Domains). ++ + ## Virtual Firmware + + ### x86/HVM iPXE +-- +2.20.1 + From c1dd47a6dbdad078cb981b3312588aeb19a28282 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 22 Sep 2020 22:12:48 +0100 Subject: [PATCH 08/11] 10 security updates, remove some stray files x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) Missing unlock in XENMEM_acquire_resource error path [XSA-334, CVE-2020-25598] (#1881616) race when migrating timers between x86 HVM vCPU-s [XSA-336, CVE-2020-25604] (#1881618) PCI passthrough code reading back hardware registers [XSA-337, CVE-2020-25595] (#1881587) once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] (#1881588) x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] (#1881617) Missing memory barriers when accessing/allocating an event channel [XSA-340, CVE-2020-25603] (#1881583) out of bounds event channels available to 32-bit x86 domains [XSA-342, CVE-2020-25600] (#1881582) races with evtchn_reset() [XSA-343, CVE-2020-25599] (#1881581) lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, CVE-2020-25601] (#1881586) --- xen.spec | 51 +++++- xsa328-1.patch | 126 --------------- xsa328-2.patch | 56 ------- xsa328-4.13-1.patch | 118 -------------- xsa328-4.13-2.patch | 48 ------ xsa333.patch | 39 +++++ xsa334-4.12.patch | 57 +++++++ xsa336.patch | 283 ++++++++++++++++++++++++++++++++ xsa337-4.12-1.patch | 92 +++++++++++ xsa337-4.12-2.patch | 182 +++++++++++++++++++++ xsa338.patch | 42 +++++ xsa339.patch | 76 +++++++++ xsa340.patch | 65 ++++++++ xsa342-4.13.patch | 145 +++++++++++++++++ xsa343-4.12-1.patch | 190 ++++++++++++++++++++++ xsa343-4.12-2.patch | 290 +++++++++++++++++++++++++++++++++ xsa343-4.12-3.patch | 381 ++++++++++++++++++++++++++++++++++++++++++++ xsa344-4.12-1.patch | 132 +++++++++++++++ xsa344-4.12-2.patch | 203 +++++++++++++++++++++++ 19 files changed, 2227 insertions(+), 349 deletions(-) delete mode 100644 xsa328-1.patch delete mode 100644 xsa328-2.patch delete mode 100644 xsa328-4.13-1.patch delete mode 100644 xsa328-4.13-2.patch create mode 100644 xsa333.patch create mode 100644 xsa334-4.12.patch create mode 100644 xsa336.patch create mode 100644 xsa337-4.12-1.patch create mode 100644 xsa337-4.12-2.patch create mode 100644 xsa338.patch create mode 100644 xsa339.patch create mode 100644 xsa340.patch create mode 100644 xsa342-4.13.patch create mode 100644 xsa343-4.12-1.patch create mode 100644 xsa343-4.12-2.patch create mode 100644 xsa343-4.12-3.patch create mode 100644 xsa344-4.12-1.patch create mode 100644 xsa344-4.12-2.patch diff --git a/xen.spec b/xen.spec index 1fc88db..f408fb2 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.3 -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 @@ -131,6 +131,20 @@ Patch67: xsa321-4.12-7.patch Patch68: xsa327.patch Patch69: xsa335-qemu.patch Patch70: xsa335-trad.patch +Patch71: xsa333.patch +Patch72: xsa334-4.12.patch +Patch73: xsa336.patch +Patch74: xsa337-4.12-1.patch +Patch75: xsa337-4.12-2.patch +Patch76: xsa338.patch +Patch77: xsa339.patch +Patch78: xsa340.patch +Patch79: xsa342-4.13.patch +Patch80: xsa343-4.12-1.patch +Patch81: xsa343-4.12-2.patch +Patch82: xsa343-4.12-3.patch +Patch83: xsa344-4.12-1.patch +Patch84: xsa344-4.12-2.patch %if %build_qemutrad @@ -344,6 +358,20 @@ manage Xen virtual machines. %patch67 -p1 %patch68 -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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -908,6 +936,27 @@ fi %endif %changelog +* Tue Sep 22 2020 Michael Young - 4.12.3-5 +- x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, + CVE-2020-25602] (#1881619) +- Missing unlock in XENMEM_acquire_resource error path [XSA-334, + CVE-2020-25598] (#1881616) +- race when migrating timers between x86 HVM vCPU-s [XSA-336, + CVE-2020-25604] (#1881618) +- PCI passthrough code reading back hardware registers [XSA-337, + CVE-2020-25595] (#1881587) +- once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] + (#1881588) +- x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] + (#1881617) +- Missing memory barriers when accessing/allocating an event channel [XSA-340, + CVE-2020-25603] (#1881583) +- out of bounds event channels available to 32-bit x86 domains [XSA-342, + CVE-2020-25600] (#1881582) +- races with evtchn_reset() [XSA-343, CVE-2020-25599] (#1881581) +- lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, + CVE-2020-25601] (#1881586) + * Tue Aug 25 2020 Michael Young - 4.12.3-4 - QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) diff --git a/xsa328-1.patch b/xsa328-1.patch deleted file mode 100644 index eb4df59..0000000 --- a/xsa328-1.patch +++ /dev/null @@ -1,126 +0,0 @@ -From: Jan Beulich -Subject: x86/EPT: ept_set_middle_entry() related adjustments - -ept_split_super_page() wants to further modify the newly allocated -table, so have ept_set_middle_entry() return the mapped pointer rather -than tearing it down and then getting re-established right again. - -Similarly ept_next_level() wants to hand back a mapped pointer of -the next level page, so re-use the one established by -ept_set_middle_entry() in case that path was taken. - -Pull the setting of suppress_ve ahead of insertion into the higher level -table, and don't have ept_split_super_page() set the field a 2nd time. - -This is part of XSA-328. - -Signed-off-by: Jan Beulich ---- - xen/arch/x86/mm/p2m-ept.c | 41 +++++++++++++++++---------------------- - 1 file changed, 18 insertions(+), 23 deletions(-) - -diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c -index 293f3e9419..d9913a6c97 100644 ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -186,8 +186,9 @@ static void ept_p2m_type_to_flags(const struct p2m_domain *p2m, - #define GUEST_TABLE_SUPER_PAGE 2 - #define GUEST_TABLE_POD_PAGE 3 - --/* Fill in middle levels of ept table */ --static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) -+/* Fill in middle level of ept table; return pointer to mapped new table. */ -+static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, -+ ept_entry_t *ept_entry) - { - mfn_t mfn; - ept_entry_t *table; -@@ -195,7 +196,12 @@ static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) - - mfn = p2m_alloc_ptp(p2m, 0); - if ( mfn_eq(mfn, INVALID_MFN) ) -- return 0; -+ return NULL; -+ -+ table = map_domain_page(mfn); -+ -+ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -+ table[i].suppress_ve = 1; - - ept_entry->epte = 0; - ept_entry->mfn = mfn_x(mfn); -@@ -207,14 +213,7 @@ static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) - - ept_entry->suppress_ve = 1; - -- table = map_domain_page(mfn); -- -- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -- table[i].suppress_ve = 1; -- -- unmap_domain_page(table); -- -- return 1; -+ return table; - } - - /* free ept sub tree behind an entry */ -@@ -252,10 +251,10 @@ static bool_t ept_split_super_page(struct p2m_domain *p2m, - - ASSERT(is_epte_superpage(ept_entry)); - -- if ( !ept_set_middle_entry(p2m, &new_ept) ) -+ table = ept_set_middle_entry(p2m, &new_ept); -+ if ( !table ) - return 0; - -- table = map_domain_page(_mfn(new_ept.mfn)); - trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); - - for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -@@ -266,7 +265,6 @@ static bool_t ept_split_super_page(struct p2m_domain *p2m, - epte->sp = (level > 1); - epte->mfn += i * trunk; - epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; -- epte->suppress_ve = 1; - - ept_p2m_type_to_flags(p2m, epte); - -@@ -305,8 +303,7 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, - ept_entry_t **table, unsigned long *gfn_remainder, - int next_level) - { -- unsigned long mfn; -- ept_entry_t *ept_entry, e; -+ ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - - shift = next_level * EPT_TABLE_ORDER; -@@ -331,19 +328,17 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- if ( !ept_set_middle_entry(p2m, ept_entry) ) -+ next = ept_set_middle_entry(p2m, ept_entry); -+ if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- else -- e = atomic_read_ept_entry(ept_entry); /* Refresh */ -+ /* e is now stale and hence may not be used anymore below. */ - } -- - /* The only time sp would be set here is if we had hit a superpage */ -- if ( is_epte_superpage(&e) ) -+ else if ( is_epte_superpage(&e) ) - return GUEST_TABLE_SUPER_PAGE; - -- mfn = e.mfn; - unmap_domain_page(*table); -- *table = map_domain_page(_mfn(mfn)); -+ *table = next ?: map_domain_page(_mfn(e.mfn)); - *gfn_remainder &= (1UL << shift) - 1; - return GUEST_TABLE_NORMAL_PAGE; - } --- -2.26.2 - diff --git a/xsa328-2.patch b/xsa328-2.patch deleted file mode 100644 index 9614feb..0000000 --- a/xsa328-2.patch +++ /dev/null @@ -1,56 +0,0 @@ -From: -Subject: x86/ept: atomically modify entries in ept_next_level - -ept_next_level was passing a live PTE pointer to ept_set_middle_entry, -which was then modified without taking into account that the PTE could -be part of a live EPT table. This wasn't a security issue because the -pages returned by p2m_alloc_ptp are zeroed, so adding such an entry -before actually initializing it didn't allow a guest to access -physical memory addresses it wasn't supposed to access. - -This is part of XSA-328. - -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm/p2m-ept.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c -index d9913a6c97..87a14f6f22 100644 ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -306,6 +306,8 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, - ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - -+ ASSERT(next_level); -+ - shift = next_level * EPT_TABLE_ORDER; - - index = *gfn_remainder >> shift; -@@ -322,16 +324,20 @@ static int ept_next_level(struct p2m_domain *p2m, bool_t read_only, - - if ( !is_epte_present(&e) ) - { -+ int rc; -+ - if ( e.sa_p2mt == p2m_populate_on_demand ) - return GUEST_TABLE_POD_PAGE; - - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- next = ept_set_middle_entry(p2m, ept_entry); -+ next = ept_set_middle_entry(p2m, &e); - if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- /* e is now stale and hence may not be used anymore below. */ -+ -+ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); -+ ASSERT(rc == 0); - } - /* The only time sp would be set here is if we had hit a superpage */ - else if ( is_epte_superpage(&e) ) --- -2.26.2 - diff --git a/xsa328-4.13-1.patch b/xsa328-4.13-1.patch deleted file mode 100644 index 56e48de..0000000 --- a/xsa328-4.13-1.patch +++ /dev/null @@ -1,118 +0,0 @@ -From: Jan Beulich -Subject: x86/EPT: ept_set_middle_entry() related adjustments - -ept_split_super_page() wants to further modify the newly allocated -table, so have ept_set_middle_entry() return the mapped pointer rather -than tearing it down and then getting re-established right again. - -Similarly ept_next_level() wants to hand back a mapped pointer of -the next level page, so re-use the one established by -ept_set_middle_entry() in case that path was taken. - -Pull the setting of suppress_ve ahead of insertion into the higher level -table, and don't have ept_split_super_page() set the field a 2nd time. - -This is part of XSA-328. - -Signed-off-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -187,8 +187,9 @@ static void ept_p2m_type_to_flags(struct - #define GUEST_TABLE_SUPER_PAGE 2 - #define GUEST_TABLE_POD_PAGE 3 - --/* Fill in middle levels of ept table */ --static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) -+/* Fill in middle level of ept table; return pointer to mapped new table. */ -+static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, -+ ept_entry_t *ept_entry) - { - mfn_t mfn; - ept_entry_t *table; -@@ -196,7 +197,12 @@ static int ept_set_middle_entry(struct p - - mfn = p2m_alloc_ptp(p2m, 0); - if ( mfn_eq(mfn, INVALID_MFN) ) -- return 0; -+ return NULL; -+ -+ table = map_domain_page(mfn); -+ -+ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -+ table[i].suppress_ve = 1; - - ept_entry->epte = 0; - ept_entry->mfn = mfn_x(mfn); -@@ -208,14 +214,7 @@ static int ept_set_middle_entry(struct p - - ept_entry->suppress_ve = 1; - -- table = map_domain_page(mfn); -- -- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -- table[i].suppress_ve = 1; -- -- unmap_domain_page(table); -- -- return 1; -+ return table; - } - - /* free ept sub tree behind an entry */ -@@ -253,10 +252,10 @@ static bool_t ept_split_super_page(struc - - ASSERT(is_epte_superpage(ept_entry)); - -- if ( !ept_set_middle_entry(p2m, &new_ept) ) -+ table = ept_set_middle_entry(p2m, &new_ept); -+ if ( !table ) - return 0; - -- table = map_domain_page(_mfn(new_ept.mfn)); - trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); - - for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -@@ -267,7 +266,6 @@ static bool_t ept_split_super_page(struc - epte->sp = (level > 1); - epte->mfn += i * trunk; - epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; -- epte->suppress_ve = 1; - - ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access); - -@@ -306,8 +304,7 @@ static int ept_next_level(struct p2m_dom - ept_entry_t **table, unsigned long *gfn_remainder, - int next_level) - { -- unsigned long mfn; -- ept_entry_t *ept_entry, e; -+ ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - - shift = next_level * EPT_TABLE_ORDER; -@@ -332,19 +329,17 @@ static int ept_next_level(struct p2m_dom - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- if ( !ept_set_middle_entry(p2m, ept_entry) ) -+ next = ept_set_middle_entry(p2m, ept_entry); -+ if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- else -- e = atomic_read_ept_entry(ept_entry); /* Refresh */ -+ /* e is now stale and hence may not be used anymore below. */ - } -- - /* The only time sp would be set here is if we had hit a superpage */ -- if ( is_epte_superpage(&e) ) -+ else if ( is_epte_superpage(&e) ) - return GUEST_TABLE_SUPER_PAGE; - -- mfn = e.mfn; - unmap_domain_page(*table); -- *table = map_domain_page(_mfn(mfn)); -+ *table = next ?: map_domain_page(_mfn(e.mfn)); - *gfn_remainder &= (1UL << shift) - 1; - return GUEST_TABLE_NORMAL_PAGE; - } diff --git a/xsa328-4.13-2.patch b/xsa328-4.13-2.patch deleted file mode 100644 index c4f437f..0000000 --- a/xsa328-4.13-2.patch +++ /dev/null @@ -1,48 +0,0 @@ -From: -Subject: x86/ept: atomically modify entries in ept_next_level - -ept_next_level was passing a live PTE pointer to ept_set_middle_entry, -which was then modified without taking into account that the PTE could -be part of a live EPT table. This wasn't a security issue because the -pages returned by p2m_alloc_ptp are zeroed, so adding such an entry -before actually initializing it didn't allow a guest to access -physical memory addresses it wasn't supposed to access. - -This is part of XSA-328. - -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -307,6 +307,8 @@ static int ept_next_level(struct p2m_dom - ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - -+ ASSERT(next_level); -+ - shift = next_level * EPT_TABLE_ORDER; - - index = *gfn_remainder >> shift; -@@ -323,16 +325,20 @@ static int ept_next_level(struct p2m_dom - - if ( !is_epte_present(&e) ) - { -+ int rc; -+ - if ( e.sa_p2mt == p2m_populate_on_demand ) - return GUEST_TABLE_POD_PAGE; - - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- next = ept_set_middle_entry(p2m, ept_entry); -+ next = ept_set_middle_entry(p2m, &e); - if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- /* e is now stale and hence may not be used anymore below. */ -+ -+ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); -+ ASSERT(rc == 0); - } - /* The only time sp would be set here is if we had hit a superpage */ - else if ( is_epte_superpage(&e) ) diff --git a/xsa333.patch b/xsa333.patch new file mode 100644 index 0000000..6b86c94 --- /dev/null +++ b/xsa333.patch @@ -0,0 +1,39 @@ +From: Andrew Cooper +Subject: x86/pv: Handle the Intel-specific MSR_MISC_ENABLE correctly + +This MSR doesn't exist on AMD hardware, and switching away from the safe +functions in the common MSR path was an erroneous change. + +Partially revert the change. + +This is XSA-333. + +Fixes: 4fdc932b3cc ("x86/Intel: drop another 32-bit leftover") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: Wei Liu + +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index efeb2a727e..6332c74b80 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -924,7 +924,8 @@ static int read_msr(unsigned int reg, uint64_t *val, + return X86EMUL_OKAY; + + case MSR_IA32_MISC_ENABLE: +- rdmsrl(reg, *val); ++ if ( rdmsr_safe(reg, *val) ) ++ break; + *val = guest_misc_enable(*val); + return X86EMUL_OKAY; + +@@ -1059,7 +1060,8 @@ static int write_msr(unsigned int reg, uint64_t val, + break; + + case MSR_IA32_MISC_ENABLE: +- rdmsrl(reg, temp); ++ if ( rdmsr_safe(reg, temp) ) ++ break; + if ( val != guest_misc_enable(temp) ) + goto invalid; + return X86EMUL_OKAY; diff --git a/xsa334-4.12.patch b/xsa334-4.12.patch new file mode 100644 index 0000000..57ec6d6 --- /dev/null +++ b/xsa334-4.12.patch @@ -0,0 +1,57 @@ +From: Andrew Cooper +Subject: xen/memory: Don't skip the RCU unlock path in acquire_resource() + +In the case that an HVM Stubdomain makes an XENMEM_acquire_resource hypercall, +the FIXME path will bypass rcu_unlock_domain() on the way out of the function. + +Move the check to the start of the function. This does change the behaviour +of the get-size path for HVM Stubdomains, but that functionality is currently +broken and unused anyway, as well as being quite useless to entities which +can't actually map the resource anyway. + +This is XSA-334. + +Fixes: 83fa6552ce ("common: add a new mappable resource type: XENMEM_resource_grant_table") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +Backport note: The deletion of the XENMEM_rsrc_acq_caller_owned clause is +correct and intentional. This was an output-only flag who's use never +survived into the Xen 4.12 release, and was subsequently deleted in Xen 4.13. + +diff --git a/xen/common/memory.c b/xen/common/memory.c +index dbc06fb0bf..ff88ebb314 100644 +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -1059,6 +1059,14 @@ static int acquire_resource( + xen_pfn_t mfn_list[32]; + int rc; + ++ /* ++ * FIXME: Until foreign pages inserted into the P2M are properly ++ * reference counted, it is unsafe to allow mapping of ++ * resource pages unless the caller is the hardware domain. ++ */ ++ if ( paging_mode_translate(currd) && !is_hardware_domain(currd) ) ++ return -EACCES; ++ + if ( copy_from_guest(&xmar, arg, 1) ) + return -EFAULT; + +@@ -1115,16 +1123,6 @@ static int acquire_resource( + xen_pfn_t gfn_list[ARRAY_SIZE(mfn_list)]; + unsigned int i; + +- /* +- * FIXME: Until foreign pages inserted into the P2M are properly +- * reference counted, it is unsafe to allow mapping of +- * non-caller-owned resource pages unless the caller is +- * the hardware domain. +- */ +- if ( !(xmar.flags & XENMEM_rsrc_acq_caller_owned) && +- !is_hardware_domain(currd) ) +- return -EACCES; +- + if ( copy_from_guest(gfn_list, xmar.frame_list, xmar.nr_frames) ) + rc = -EFAULT; + diff --git a/xsa336.patch b/xsa336.patch new file mode 100644 index 0000000..b44c298 --- /dev/null +++ b/xsa336.patch @@ -0,0 +1,283 @@ +From: Roger Pau MonnĂ© +Subject: x86/vpt: fix race when migrating timers between vCPUs + +The current vPT code will migrate the emulated timers between vCPUs +(change the pt->vcpu field) while just holding the destination lock, +either from create_periodic_time or pt_adjust_global_vcpu_target if +the global target is adjusted. Changing the periodic_timer vCPU field +in this way creates a race where a third party could grab the lock in +the unlocked region of pt_adjust_global_vcpu_target (or before +create_periodic_time performs the vcpu change) and then release the +lock from a different vCPU, creating a locking imbalance. + +Introduce a per-domain rwlock in order to protect periodic_time +migration between vCPU lists. Taking the lock in read mode prevents +any timer from being migrated to a different vCPU, while taking it in +write mode allows performing migration of timers across vCPUs. The +per-vcpu locks are still used to protect all the other fields from the +periodic_timer struct. + +Note that such migration shouldn't happen frequently, and hence +there's no performance drop as a result of such locking. + +This is XSA-336. + +Reported-by: Igor Druzhinin +Tested-by: Igor Druzhinin +Signed-off-by: Roger Pau MonnĂ© +Reviewed-by: Jan Beulich +--- +Changes since v2: + - Re-order pt_adjust_vcpu to remove one if. + - Fix pt_lock to not call pt_vcpu_lock, as we might end up using a + stale value of pt->vcpu when taking the per-vcpu lock. + +Changes since v1: + - Use a per-domain rwlock to protect timer vCPU migration. + +--- a/xen/arch/x86/hvm/hvm.c ++++ b/xen/arch/x86/hvm/hvm.c +@@ -658,6 +658,8 @@ int hvm_domain_initialise(struct domain + /* need link to containing domain */ + d->arch.hvm.pl_time->domain = d; + ++ rwlock_init(&d->arch.hvm.pl_time->pt_migrate); ++ + /* Set the default IO Bitmap. */ + if ( is_hardware_domain(d) ) + { +--- a/xen/arch/x86/hvm/vpt.c ++++ b/xen/arch/x86/hvm/vpt.c +@@ -153,23 +153,32 @@ static int pt_irq_masked(struct periodic + return 1; + } + +-static void pt_lock(struct periodic_time *pt) ++static void pt_vcpu_lock(struct vcpu *v) + { +- struct vcpu *v; ++ read_lock(&v->domain->arch.hvm.pl_time->pt_migrate); ++ spin_lock(&v->arch.hvm.tm_lock); ++} + +- for ( ; ; ) +- { +- v = pt->vcpu; +- spin_lock(&v->arch.hvm.tm_lock); +- if ( likely(pt->vcpu == v) ) +- break; +- spin_unlock(&v->arch.hvm.tm_lock); +- } ++static void pt_vcpu_unlock(struct vcpu *v) ++{ ++ spin_unlock(&v->arch.hvm.tm_lock); ++ read_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); ++} ++ ++static void pt_lock(struct periodic_time *pt) ++{ ++ /* ++ * We cannot use pt_vcpu_lock here, because we need to acquire the ++ * per-domain lock first and then (re-)fetch the value of pt->vcpu, or ++ * else we might be using a stale value of pt->vcpu. ++ */ ++ read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); ++ spin_lock(&pt->vcpu->arch.hvm.tm_lock); + } + + static void pt_unlock(struct periodic_time *pt) + { +- spin_unlock(&pt->vcpu->arch.hvm.tm_lock); ++ pt_vcpu_unlock(pt->vcpu); + } + + static void pt_process_missed_ticks(struct periodic_time *pt) +@@ -219,7 +228,7 @@ void pt_save_timer(struct vcpu *v) + if ( v->pause_flags & VPF_blocked ) + return; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + if ( !pt->do_not_freeze ) +@@ -227,7 +236,7 @@ void pt_save_timer(struct vcpu *v) + + pt_freeze_time(v); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + void pt_restore_timer(struct vcpu *v) +@@ -235,7 +244,7 @@ void pt_restore_timer(struct vcpu *v) + struct list_head *head = &v->arch.hvm.tm_list; + struct periodic_time *pt; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + { +@@ -248,7 +257,7 @@ void pt_restore_timer(struct vcpu *v) + + pt_thaw_time(v); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + static void pt_timer_fn(void *data) +@@ -309,7 +318,7 @@ int pt_update_irq(struct vcpu *v) + int irq, pt_vector = -1; + bool level; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + earliest_pt = NULL; + max_lag = -1ULL; +@@ -339,7 +348,7 @@ int pt_update_irq(struct vcpu *v) + + if ( earliest_pt == NULL ) + { +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + return -1; + } + +@@ -347,7 +356,7 @@ int pt_update_irq(struct vcpu *v) + irq = earliest_pt->irq; + level = earliest_pt->level; + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + switch ( earliest_pt->source ) + { +@@ -394,7 +403,7 @@ int pt_update_irq(struct vcpu *v) + time_cb *cb = NULL; + void *cb_priv; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + /* Make sure the timer is still on the list. */ + list_for_each_entry ( pt, &v->arch.hvm.tm_list, list ) + if ( pt == earliest_pt ) +@@ -404,7 +413,7 @@ int pt_update_irq(struct vcpu *v) + cb_priv = pt->priv; + break; + } +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + if ( cb != NULL ) + cb(v, cb_priv); +@@ -441,12 +450,12 @@ void pt_intr_post(struct vcpu *v, struct + if ( intack.source == hvm_intsrc_vector ) + return; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + pt = is_pt_irq(v, intack); + if ( pt == NULL ) + { +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + return; + } + +@@ -455,7 +464,7 @@ void pt_intr_post(struct vcpu *v, struct + cb = pt->cb; + cb_priv = pt->priv; + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + if ( cb != NULL ) + cb(v, cb_priv); +@@ -466,12 +475,12 @@ void pt_migrate(struct vcpu *v) + struct list_head *head = &v->arch.hvm.tm_list; + struct periodic_time *pt; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + migrate_timer(&pt->timer, v->processor); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + void create_periodic_time( +@@ -490,7 +499,7 @@ void create_periodic_time( + + destroy_periodic_time(pt); + +- spin_lock(&v->arch.hvm.tm_lock); ++ write_lock(&v->domain->arch.hvm.pl_time->pt_migrate); + + pt->pending_intr_nr = 0; + pt->do_not_freeze = 0; +@@ -540,7 +549,7 @@ void create_periodic_time( + init_timer(&pt->timer, pt_timer_fn, pt, v->processor); + set_timer(&pt->timer, pt->scheduled); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ write_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); + } + + void destroy_periodic_time(struct periodic_time *pt) +@@ -565,30 +574,20 @@ void destroy_periodic_time(struct period + + static void pt_adjust_vcpu(struct periodic_time *pt, struct vcpu *v) + { +- int on_list; +- + ASSERT(pt->source == PTSRC_isa || pt->source == PTSRC_ioapic); + + if ( pt->vcpu == NULL ) + return; + +- pt_lock(pt); +- on_list = pt->on_list; +- if ( pt->on_list ) +- list_del(&pt->list); +- pt->on_list = 0; +- pt_unlock(pt); +- +- spin_lock(&v->arch.hvm.tm_lock); ++ write_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + pt->vcpu = v; +- if ( on_list ) ++ if ( pt->on_list ) + { +- pt->on_list = 1; ++ list_del(&pt->list); + list_add(&pt->list, &v->arch.hvm.tm_list); +- + migrate_timer(&pt->timer, v->processor); + } +- spin_unlock(&v->arch.hvm.tm_lock); ++ write_unlock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + } + + void pt_adjust_global_vcpu_target(struct vcpu *v) +--- a/xen/include/asm-x86/hvm/vpt.h ++++ b/xen/include/asm-x86/hvm/vpt.h +@@ -128,6 +128,13 @@ struct pl_time { /* platform time */ + struct RTCState vrtc; + struct HPETState vhpet; + struct PMTState vpmt; ++ /* ++ * rwlock to prevent periodic_time vCPU migration. Take the lock in read ++ * mode in order to prevent the vcpu field of periodic_time from changing. ++ * Lock must be taken in write mode when changes to the vcpu field are ++ * performed, as it allows exclusive access to all the timers of a domain. ++ */ ++ rwlock_t pt_migrate; + /* guest_time = Xen sys time + stime_offset */ + int64_t stime_offset; + /* Ensures monotonicity in appropriate timer modes. */ diff --git a/xsa337-4.12-1.patch b/xsa337-4.12-1.patch new file mode 100644 index 0000000..c8d3b1f --- /dev/null +++ b/xsa337-4.12-1.patch @@ -0,0 +1,92 @@ +From: Roger Pau MonnĂ© +Subject: x86/msi: get rid of read_msi_msg + +It's safer and faster to just use the cached last written +(untranslated) MSI message stored in msi_desc for the single user that +calls read_msi_msg. + +This also prevents relying on the data read from the device MSI +registers in order to figure out the index into the IOMMU interrupt +remapping table, which is not safe. + +This is part of XSA-337. + +Reported-by: Andrew Cooper +Requested-by: Andrew Cooper +Signed-off-by: Roger Pau MonnĂ© +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/msi.c ++++ b/xen/arch/x86/msi.c +@@ -192,59 +192,6 @@ void msi_compose_msg(unsigned vector, co + MSI_DATA_VECTOR(vector); + } + +-static bool read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +-{ +- switch ( entry->msi_attrib.type ) +- { +- case PCI_CAP_ID_MSI: +- { +- struct pci_dev *dev = entry->dev; +- int pos = entry->msi_attrib.pos; +- u16 data, seg = dev->seg; +- u8 bus = dev->bus; +- u8 slot = PCI_SLOT(dev->devfn); +- u8 func = PCI_FUNC(dev->devfn); +- +- msg->address_lo = pci_conf_read32(seg, bus, slot, func, +- msi_lower_address_reg(pos)); +- if ( entry->msi_attrib.is_64 ) +- { +- msg->address_hi = pci_conf_read32(seg, bus, slot, func, +- msi_upper_address_reg(pos)); +- data = pci_conf_read16(seg, bus, slot, func, +- msi_data_reg(pos, 1)); +- } +- else +- { +- msg->address_hi = 0; +- data = pci_conf_read16(seg, bus, slot, func, +- msi_data_reg(pos, 0)); +- } +- msg->data = data; +- break; +- } +- case PCI_CAP_ID_MSIX: +- { +- void __iomem *base = entry->mask_base; +- +- if ( unlikely(!msix_memory_decoded(entry->dev, +- entry->msi_attrib.pos)) ) +- return false; +- msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); +- msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); +- msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); +- break; +- } +- default: +- BUG(); +- } +- +- if ( iommu_intremap ) +- iommu_read_msi_from_ire(entry, msg); +- +- return true; +-} +- + static int write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) + { + entry->msg = *msg; +@@ -322,10 +269,7 @@ void set_msi_affinity(struct irq_desc *d + + ASSERT(spin_is_locked(&desc->lock)); + +- memset(&msg, 0, sizeof(msg)); +- if ( !read_msi_msg(msi_desc, &msg) ) +- return; +- ++ msg = msi_desc->msg; + msg.data &= ~MSI_DATA_VECTOR_MASK; + msg.data |= MSI_DATA_VECTOR(desc->arch.vector); + msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; diff --git a/xsa337-4.12-2.patch b/xsa337-4.12-2.patch new file mode 100644 index 0000000..aa2fb57 --- /dev/null +++ b/xsa337-4.12-2.patch @@ -0,0 +1,182 @@ +From: Jan Beulich +Subject: x86/MSI-X: restrict reading of table/PBA bases from BARs + +When assigned to less trusted or un-trusted guests, devices may change +state behind our backs (they may e.g. get reset by means we may not know +about). Therefore we should avoid reading BARs from hardware once a +device is no longer owned by Dom0. Furthermore when we can't read a BAR, +or when we read zero, we shouldn't instead use the caller provided +address unless that caller can be trusted. + +Re-arrange the logic in msix_capability_init() such that only Dom0 (and +only if the device isn't DomU-owned yet) or calls through +PHYSDEVOP_prepare_msix will actually result in the reading of the +respective BAR register(s). Additionally do so only as long as in-use +table entries are known (note that invocation of PHYSDEVOP_prepare_msix +counts as a "pseudo" entry). In all other uses the value already +recorded will get used instead. + +Clear the recorded values in _pci_cleanup_msix() as well as on the one +affected error path. (Adjust this error path to also avoid blindly +disabling MSI-X when it was enabled on entry to the function.) + +While moving around variable declarations (in many cases to reduce their +scopes), also adjust some of their types. + +This is part of XSA-337. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau MonnĂ© + +--- a/xen/arch/x86/msi.c ++++ b/xen/arch/x86/msi.c +@@ -790,16 +790,14 @@ static int msix_capability_init(struct p + { + struct arch_msix *msix = dev->msix; + struct msi_desc *entry = NULL; +- int vf; + u16 control; + u64 table_paddr; + u32 table_offset; +- u8 bir, pbus, pslot, pfunc; + u16 seg = dev->seg; + u8 bus = dev->bus; + u8 slot = PCI_SLOT(dev->devfn); + u8 func = PCI_FUNC(dev->devfn); +- bool maskall = msix->host_maskall; ++ bool maskall = msix->host_maskall, zap_on_error = false; + + ASSERT(pcidevs_locked()); + +@@ -837,43 +835,45 @@ static int msix_capability_init(struct p + /* Locate MSI-X table region */ + table_offset = pci_conf_read32(seg, bus, slot, func, + msix_table_offset_reg(pos)); +- bir = (u8)(table_offset & PCI_MSIX_BIRMASK); +- table_offset &= ~PCI_MSIX_BIRMASK; ++ if ( !msix->used_entries && ++ (!msi || ++ (is_hardware_domain(current->domain) && ++ (dev->domain == current->domain || dev->domain == dom_io))) ) ++ { ++ unsigned int bir = table_offset & PCI_MSIX_BIRMASK, pbus, pslot, pfunc; ++ int vf; ++ paddr_t pba_paddr; ++ unsigned int pba_offset; + +- if ( !dev->info.is_virtfn ) +- { +- pbus = bus; +- pslot = slot; +- pfunc = func; +- vf = -1; +- } +- else +- { +- pbus = dev->info.physfn.bus; +- pslot = PCI_SLOT(dev->info.physfn.devfn); +- pfunc = PCI_FUNC(dev->info.physfn.devfn); +- vf = PCI_BDF2(dev->bus, dev->devfn); +- } +- +- table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); +- WARN_ON(msi && msi->table_base != table_paddr); +- if ( !table_paddr ) +- { +- if ( !msi || !msi->table_base ) ++ if ( !dev->info.is_virtfn ) + { +- pci_conf_write16(seg, bus, slot, func, msix_control_reg(pos), +- control & ~PCI_MSIX_FLAGS_ENABLE); +- xfree(entry); +- return -ENXIO; ++ pbus = bus; ++ pslot = slot; ++ pfunc = func; ++ vf = -1; ++ } ++ else ++ { ++ pbus = dev->info.physfn.bus; ++ pslot = PCI_SLOT(dev->info.physfn.devfn); ++ pfunc = PCI_FUNC(dev->info.physfn.devfn); ++ vf = PCI_BDF2(dev->bus, dev->devfn); + } +- table_paddr = msi->table_base; +- } +- table_paddr += table_offset; + +- if ( !msix->used_entries ) +- { +- u64 pba_paddr; +- u32 pba_offset; ++ table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); ++ WARN_ON(msi && msi->table_base != table_paddr); ++ if ( !table_paddr ) ++ { ++ if ( !msi || !msi->table_base ) ++ { ++ pci_conf_write16(seg, bus, slot, func, msix_control_reg(pos), ++ control & ~PCI_MSIX_FLAGS_ENABLE); ++ xfree(entry); ++ return -ENXIO; ++ } ++ table_paddr = msi->table_base; ++ } ++ table_paddr += table_offset & ~PCI_MSIX_BIRMASK; + + msix->nr_entries = nr_entries; + msix->table.first = PFN_DOWN(table_paddr); +@@ -894,7 +894,19 @@ static int msix_capability_init(struct p + BITS_TO_LONGS(nr_entries) - 1); + WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, msix->pba.first, + msix->pba.last)); ++ ++ zap_on_error = true; + } ++ else if ( !msix->table.first ) ++ { ++ pci_conf_write16(seg, bus, slot, func, msix_control_reg(pos), ++ control); ++ xfree(entry); ++ return -ENODATA; ++ } ++ else ++ table_paddr = (msix->table.first << PAGE_SHIFT) + ++ (table_offset & ~PCI_MSIX_BIRMASK & ~PAGE_MASK); + + if ( entry ) + { +@@ -905,8 +917,16 @@ static int msix_capability_init(struct p + + if ( idx < 0 ) + { ++ if ( zap_on_error ) ++ { ++ msix->table.first = 0; ++ msix->pba.first = 0; ++ ++ control &= ~PCI_MSIX_FLAGS_ENABLE; ++ } ++ + pci_conf_write16(seg, bus, slot, func, msix_control_reg(pos), +- control & ~PCI_MSIX_FLAGS_ENABLE); ++ control); + xfree(entry); + return idx; + } +@@ -1102,9 +1122,14 @@ static void _pci_cleanup_msix(struct arc + if ( rangeset_remove_range(mmio_ro_ranges, msix->table.first, + msix->table.last) ) + WARN(); ++ msix->table.first = 0; ++ msix->table.last = 0; ++ + if ( rangeset_remove_range(mmio_ro_ranges, msix->pba.first, + msix->pba.last) ) + WARN(); ++ msix->pba.first = 0; ++ msix->pba.last = 0; + } + } + diff --git a/xsa338.patch b/xsa338.patch new file mode 100644 index 0000000..7765219 --- /dev/null +++ b/xsa338.patch @@ -0,0 +1,42 @@ +From: Jan Beulich +Subject: evtchn: relax port_is_valid() + +To avoid ports potentially becoming invalid behind the back of certain +other functions (due to ->max_evtchn shrinking) because of +- a guest invoking evtchn_reset() and from a 2nd vCPU opening new + channels in parallel (see also XSA-343), +- alloc_unbound_xen_event_channel() produced channels living above the + 2-level range (see also XSA-342), +drop the max_evtchns check from port_is_valid(). For a port for which +the function once returned "true", the returned value may not turn into +"false" later on. The function's result may only depend on bounds which +can only ever grow (which is the case for d->valid_evtchns). + +This also eliminates a false sense of safety, utilized by some of the +users (see again XSA-343): Without a suitable lock held, d->max_evtchns +may change at any time, and hence deducing that certain other operations +are safe when port_is_valid() returned true is not legitimate. The +opportunities to abuse this may get widened by the change here +(depending on guest and host configuration), but will be taken care of +by the other XSA. + +This is XSA-338. + +Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall +--- +v5: New, split from larger patch. + +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -107,8 +107,6 @@ void notify_via_xen_event_channel(struct + + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { +- if ( p >= d->max_evtchns ) +- return 0; + return p < read_atomic(&d->valid_evtchns); + } + diff --git a/xsa339.patch b/xsa339.patch new file mode 100644 index 0000000..3311ae0 --- /dev/null +++ b/xsa339.patch @@ -0,0 +1,76 @@ +From: Andrew Cooper +Subject: x86/pv: Avoid double exception injection + +There is at least one path (SYSENTER with NT set, Xen converts to #GP) which +ends up injecting the #GP fault twice, first in compat_sysenter(), and then a +second time in compat_test_all_events(), due to the stale TBF_EXCEPTION left +in TRAPBOUNCE_flags. + +The guest kernel sees the second fault first, which is a kernel level #GP +pointing at the head of the #GP handler, and is therefore a userspace +trigger-able DoS. + +This particular bug has bitten us several times before, so rearrange +{compat_,}create_bounce_frame() to clobber TRAPBOUNCE on success, rather than +leaving this task to one area of code which isn't used uniformly. + +Other scenarios which might result in a double injection (e.g. two calls +directly to compat_create_bounce_frame) will now crash the guest, which is far +more obvious than letting the kernel run with corrupt state. + +This is XSA-339 + +Fixes: fdac9515607b ("x86: clear EFLAGS.NT in SYSENTER entry path") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index c3e62f8734..73619f57ca 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -78,7 +78,6 @@ compat_process_softirqs: + sti + .Lcompat_bounce_exception: + call compat_create_bounce_frame +- movb $0, TRAPBOUNCE_flags(%rdx) + jmp compat_test_all_events + + ALIGN +@@ -352,7 +351,13 @@ __UNLIKELY_END(compat_bounce_null_selector) + movl %eax,UREGS_cs+8(%rsp) + movl TRAPBOUNCE_eip(%rdx),%eax + movl %eax,UREGS_rip+8(%rsp) ++ ++ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ ++ xor %eax, %eax ++ mov %ax, TRAPBOUNCE_cs(%rdx) ++ mov %al, TRAPBOUNCE_flags(%rdx) + ret ++ + .section .fixup,"ax" + .Lfx13: + xorl %edi,%edi +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 1e880eb9f6..71a00e846b 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -90,7 +90,6 @@ process_softirqs: + sti + .Lbounce_exception: + call create_bounce_frame +- movb $0, TRAPBOUNCE_flags(%rdx) + jmp test_all_events + + ALIGN +@@ -512,6 +511,11 @@ UNLIKELY_START(z, create_bounce_frame_bad_bounce_ip) + jmp asm_domain_crash_synchronous /* Does not return */ + __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) + movq %rax,UREGS_rip+8(%rsp) ++ ++ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ ++ xor %eax, %eax ++ mov %rax, TRAPBOUNCE_eip(%rdx) ++ mov %al, TRAPBOUNCE_flags(%rdx) + ret + + .pushsection .fixup, "ax", @progbits diff --git a/xsa340.patch b/xsa340.patch new file mode 100644 index 0000000..38d04da --- /dev/null +++ b/xsa340.patch @@ -0,0 +1,65 @@ +From: Julien Grall +Subject: xen/evtchn: Add missing barriers when accessing/allocating an event channel + +While the allocation of a bucket is always performed with the per-domain +lock, the bucket may be accessed without the lock taken (for instance, see +evtchn_send()). + +Instead such sites relies on port_is_valid() to return a non-zero value +when the port has a struct evtchn associated to it. The function will +mostly check whether the port is less than d->valid_evtchns as all the +buckets/event channels should be allocated up to that point. + +Unfortunately a compiler is free to re-order the assignment in +evtchn_allocate_port() so it would be possible to have d->valid_evtchns +updated before the new bucket has finish to allocate. + +Additionally on Arm, even if this was compiled "correctly", the +processor can still re-order the memory access. + +Add a write memory barrier in the allocation side and a read memory +barrier when the port is valid to prevent any re-ordering issue. + +This is XSA-340. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -178,6 +178,13 @@ int evtchn_allocate_port(struct domain * + return -ENOMEM; + bucket_from_port(d, port) = chn; + ++ /* ++ * d->valid_evtchns is used to check whether the bucket can be ++ * accessed without the per-domain lock. Therefore, ++ * d->valid_evtchns should be seen *after* the new bucket has ++ * been setup. ++ */ ++ smp_wmb(); + write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); + } + +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -107,7 +107,17 @@ void notify_via_xen_event_channel(struct + + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { +- return p < read_atomic(&d->valid_evtchns); ++ if ( p >= read_atomic(&d->valid_evtchns) ) ++ return false; ++ ++ /* ++ * The caller will usually access the event channel afterwards and ++ * may be done without taking the per-domain lock. The barrier is ++ * going in pair the smp_wmb() barrier in evtchn_allocate_port(). ++ */ ++ smp_rmb(); ++ ++ return true; + } + + static inline struct evtchn *evtchn_from_port(struct domain *d, unsigned int p) diff --git a/xsa342-4.13.patch b/xsa342-4.13.patch new file mode 100644 index 0000000..334baf1 --- /dev/null +++ b/xsa342-4.13.patch @@ -0,0 +1,145 @@ +From: Jan Beulich +Subject: evtchn/x86: enforce correct upper limit for 32-bit guests + +The recording of d->max_evtchns in evtchn_2l_init(), in particular with +the limited set of callers of the function, is insufficient. Neither for +PV nor for HVM guests the bitness is known at domain_create() time, yet +the upper bound in 2-level mode depends upon guest bitness. Recording +too high a limit "allows" x86 32-bit domains to open not properly usable +event channels, management of which (inside Xen) would then result in +corruption of the shared info and vCPU info structures. + +Keep the upper limit dynamic for the 2-level case, introducing a helper +function to retrieve the effective limit. This helper is now supposed to +be private to the event channel code. The used in do_poll() and +domain_dump_evtchn_info() weren't consistent with port uses elsewhere +and hence get switched to port_is_valid(). + +Furthermore FIFO mode's setup_ports() gets adjusted to loop only up to +the prior ABI limit, rather than all the way up to the new one. + +Finally a word on the change to do_poll(): Accessing ->max_evtchns +without holding a suitable lock was never safe, as it as well as +->evtchn_port_ops may change behind do_poll()'s back. Using +port_is_valid() instead widens some the window for potential abuse, +until we've dealt with the race altogether (see XSA-343). + +This is XSA-342. + +Reported-by: Julien Grall +Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall + +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -103,7 +103,6 @@ static const struct evtchn_port_ops evtc + void evtchn_2l_init(struct domain *d) + { + d->evtchn_port_ops = &evtchn_port_ops_2l; +- d->max_evtchns = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + } + + /* +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -151,7 +151,7 @@ static void free_evtchn_bucket(struct do + + int evtchn_allocate_port(struct domain *d, evtchn_port_t port) + { +- if ( port > d->max_evtchn_port || port >= d->max_evtchns ) ++ if ( port > d->max_evtchn_port || port >= max_evtchns(d) ) + return -ENOSPC; + + if ( port_is_valid(d, port) ) +@@ -1396,13 +1396,11 @@ static void domain_dump_evtchn_info(stru + + spin_lock(&d->event_lock); + +- for ( port = 1; port < d->max_evtchns; ++port ) ++ for ( port = 1; port_is_valid(d, port); ++port ) + { + const struct evtchn *chn; + char *ssid; + +- if ( !port_is_valid(d, port) ) +- continue; + chn = evtchn_from_port(d, port); + if ( chn->state == ECS_FREE ) + continue; +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -478,7 +478,7 @@ static void cleanup_event_array(struct d + d->evtchn_fifo = NULL; + } + +-static void setup_ports(struct domain *d) ++static void setup_ports(struct domain *d, unsigned int prev_evtchns) + { + unsigned int port; + +@@ -488,7 +488,7 @@ static void setup_ports(struct domain *d + * - save its pending state. + * - set default priority. + */ +- for ( port = 1; port < d->max_evtchns; port++ ) ++ for ( port = 1; port < prev_evtchns; port++ ) + { + struct evtchn *evtchn; + +@@ -546,6 +546,8 @@ int evtchn_fifo_init_control(struct evtc + if ( !d->evtchn_fifo ) + { + struct vcpu *vcb; ++ /* Latch the value before it changes during setup_event_array(). */ ++ unsigned int prev_evtchns = max_evtchns(d); + + for_each_vcpu ( d, vcb ) { + rc = setup_control_block(vcb); +@@ -562,8 +564,7 @@ int evtchn_fifo_init_control(struct evtc + goto error; + + d->evtchn_port_ops = &evtchn_port_ops_fifo; +- d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS; +- setup_ports(d); ++ setup_ports(d, prev_evtchns); + } + else + rc = map_control_block(v, gfn, offset); +--- a/xen/common/schedule.c ++++ b/xen/common/schedule.c +@@ -1434,7 +1434,7 @@ static long do_poll(struct sched_poll *s + goto out; + + rc = -EINVAL; +- if ( port >= d->max_evtchns ) ++ if ( !port_is_valid(d, port) ) + goto out; + + rc = 0; +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -105,6 +105,12 @@ void notify_via_xen_event_channel(struct + #define bucket_from_port(d, p) \ + ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) + ++static inline unsigned int max_evtchns(const struct domain *d) ++{ ++ return d->evtchn_fifo ? EVTCHN_FIFO_NR_CHANNELS ++ : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ++} ++ + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { + if ( p >= read_atomic(&d->valid_evtchns) ) +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -382,7 +382,6 @@ struct domain + /* Event channel information. */ + struct evtchn *evtchn; /* first bucket only */ + struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ +- unsigned int max_evtchns; /* number supported by ABI */ + unsigned int max_evtchn_port; /* max permitted port number */ + unsigned int valid_evtchns; /* number of allocated event channels */ + spinlock_t event_lock; diff --git a/xsa343-4.12-1.patch b/xsa343-4.12-1.patch new file mode 100644 index 0000000..c164b62 --- /dev/null +++ b/xsa343-4.12-1.patch @@ -0,0 +1,190 @@ +From: Jan Beulich +Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports + +While the function closes all ports, it does so without holding any +lock, and hence racing requests may be issued causing new ports to get +opened. This would have been problematic in particular if such a newly +opened port had a port number above the new implementation limit (i.e. +when switching from FIFO to 2-level) after the reset, as prior to +"evtchn: relax port_is_valid()" this could have led to e.g. +evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. + +Introduce a counter of active ports and check that it's (still) no +larger then the number of Xen internally used ones after obtaining the +necessary lock in evtchn_reset(). + +As to the access model of the new {active,xen}_evtchns fields - while +all writes get done using write_atomic(), reads ought to use +read_atomic() only when outside of a suitably locked region. + +Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have +a need to call check_free_port(). + +This is part of XSA-343. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -188,6 +188,8 @@ int evtchn_allocate_port(struct domain * + write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); + } + ++ write_atomic(&d->active_evtchns, d->active_evtchns + 1); ++ + return 0; + } + +@@ -211,11 +213,26 @@ static int get_free_port(struct domain * + return -ENOSPC; + } + ++/* ++ * Check whether a port is still marked free, and if so update the domain ++ * counter accordingly. To be used on function exit paths. ++ */ ++static void check_free_port(struct domain *d, evtchn_port_t port) ++{ ++ if ( port_is_valid(d, port) && ++ evtchn_from_port(d, port)->state == ECS_FREE ) ++ write_atomic(&d->active_evtchns, d->active_evtchns - 1); ++} ++ + void evtchn_free(struct domain *d, struct evtchn *chn) + { + /* Clear pending event to avoid unexpected behavior on re-bind. */ + evtchn_port_clear_pending(d, chn); + ++ if ( consumer_is_xen(chn) ) ++ write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); ++ write_atomic(&d->active_evtchns, d->active_evtchns - 1); ++ + /* Reset binding to vcpu0 when the channel is freed. */ + chn->state = ECS_FREE; + chn->notify_vcpu_id = 0; +@@ -258,6 +275,7 @@ static long evtchn_alloc_unbound(evtchn_ + alloc->port = port; + + out: ++ check_free_port(d, port); + spin_unlock(&d->event_lock); + rcu_unlock_domain(d); + +@@ -351,6 +369,7 @@ static long evtchn_bind_interdomain(evtc + bind->local_port = lport; + + out: ++ check_free_port(ld, lport); + spin_unlock(&ld->event_lock); + if ( ld != rd ) + spin_unlock(&rd->event_lock); +@@ -488,7 +507,7 @@ static long evtchn_bind_pirq(evtchn_bind + struct domain *d = current->domain; + struct vcpu *v = d->vcpu[0]; + struct pirq *info; +- int port, pirq = bind->pirq; ++ int port = 0, pirq = bind->pirq; + long rc; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) +@@ -536,6 +555,7 @@ static long evtchn_bind_pirq(evtchn_bind + arch_evtchn_bind_pirq(d, pirq); + + out: ++ check_free_port(d, port); + spin_unlock(&d->event_lock); + + return rc; +@@ -1011,10 +1031,10 @@ int evtchn_unmask(unsigned int port) + return 0; + } + +- + int evtchn_reset(struct domain *d) + { + unsigned int i; ++ int rc = 0; + + if ( d != current->domain && !d->controller_pause_count ) + return -EINVAL; +@@ -1024,7 +1044,9 @@ int evtchn_reset(struct domain *d) + + spin_lock(&d->event_lock); + +- if ( d->evtchn_fifo ) ++ if ( d->active_evtchns > d->xen_evtchns ) ++ rc = -EAGAIN; ++ else if ( d->evtchn_fifo ) + { + /* Switching back to 2-level ABI. */ + evtchn_fifo_destroy(d); +@@ -1033,7 +1055,7 @@ int evtchn_reset(struct domain *d) + + spin_unlock(&d->event_lock); + +- return 0; ++ return rc; + } + + static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) +@@ -1219,10 +1241,9 @@ int alloc_unbound_xen_event_channel( + + spin_lock(&ld->event_lock); + +- rc = get_free_port(ld); ++ port = rc = get_free_port(ld); + if ( rc < 0 ) + goto out; +- port = rc; + chn = evtchn_from_port(ld, port); + + rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); +@@ -1238,7 +1259,10 @@ int alloc_unbound_xen_event_channel( + + spin_unlock(&chn->lock); + ++ write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); ++ + out: ++ check_free_port(ld, port); + spin_unlock(&ld->event_lock); + + return rc < 0 ? rc : port; +@@ -1314,6 +1338,7 @@ int evtchn_init(struct domain *d, unsign + return -EINVAL; + } + evtchn_from_port(d, 0)->state = ECS_RESERVED; ++ write_atomic(&d->active_evtchns, 0); + + #if MAX_VIRT_CPUS > BITS_PER_LONG + d->poll_mask = xzalloc_array(unsigned long, BITS_TO_LONGS(d->max_vcpus)); +@@ -1340,6 +1365,8 @@ void evtchn_destroy(struct domain *d) + for ( i = 0; port_is_valid(d, i); i++ ) + evtchn_close(d, i, 0); + ++ ASSERT(!d->active_evtchns); ++ + clear_global_virq_handlers(d); + + evtchn_fifo_destroy(d); +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -346,6 +346,16 @@ struct domain + struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ + unsigned int max_evtchn_port; /* max permitted port number */ + unsigned int valid_evtchns; /* number of allocated event channels */ ++ /* ++ * Number of in-use event channels. Writers should use write_atomic(). ++ * Readers need to use read_atomic() only when not holding event_lock. ++ */ ++ unsigned int active_evtchns; ++ /* ++ * Number of event channels used internally by Xen (not subject to ++ * EVTCHNOP_reset). Read/write access like for active_evtchns. ++ */ ++ unsigned int xen_evtchns; + spinlock_t event_lock; + const struct evtchn_port_ops *evtchn_port_ops; + struct evtchn_fifo_domain *evtchn_fifo; diff --git a/xsa343-4.12-2.patch b/xsa343-4.12-2.patch new file mode 100644 index 0000000..6032d91 --- /dev/null +++ b/xsa343-4.12-2.patch @@ -0,0 +1,290 @@ +From: Jan Beulich +Subject: evtchn: convert per-channel lock to be IRQ-safe + +... in order for send_guest_{global,vcpu}_virq() to be able to make use +of it. + +This is part of XSA-343. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -248,6 +248,7 @@ static long evtchn_alloc_unbound(evtchn_ + int port; + domid_t dom = alloc->dom; + long rc; ++ unsigned long flags; + + d = rcu_lock_domain_by_any_id(dom); + if ( d == NULL ) +@@ -263,14 +264,14 @@ static long evtchn_alloc_unbound(evtchn_ + if ( rc ) + goto out; + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_UNBOUND; + if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) + chn->u.unbound.remote_domid = current->domain->domain_id; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + alloc->port = port; + +@@ -283,26 +284,32 @@ static long evtchn_alloc_unbound(evtchn_ + } + + +-static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) ++static unsigned long double_evtchn_lock(struct evtchn *lchn, ++ struct evtchn *rchn) + { +- if ( lchn < rchn ) ++ unsigned long flags; ++ ++ if ( lchn <= rchn ) + { +- spin_lock(&lchn->lock); +- spin_lock(&rchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); ++ if ( lchn != rchn ) ++ spin_lock(&rchn->lock); + } + else + { +- if ( lchn != rchn ) +- spin_lock(&rchn->lock); ++ spin_lock_irqsave(&rchn->lock, flags); + spin_lock(&lchn->lock); + } ++ ++ return flags; + } + +-static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) ++static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, ++ unsigned long flags) + { +- spin_unlock(&lchn->lock); + if ( lchn != rchn ) +- spin_unlock(&rchn->lock); ++ spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&rchn->lock, flags); + } + + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) +@@ -312,6 +319,7 @@ static long evtchn_bind_interdomain(evtc + int lport, rport = bind->remote_port; + domid_t rdom = bind->remote_dom; + long rc; ++ unsigned long flags; + + if ( rdom == DOMID_SELF ) + rdom = current->domain->domain_id; +@@ -347,7 +355,7 @@ static long evtchn_bind_interdomain(evtc + if ( rc ) + goto out; + +- double_evtchn_lock(lchn, rchn); ++ flags = double_evtchn_lock(lchn, rchn); + + lchn->u.interdomain.remote_dom = rd; + lchn->u.interdomain.remote_port = rport; +@@ -364,7 +372,7 @@ static long evtchn_bind_interdomain(evtc + */ + evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); + +- double_evtchn_unlock(lchn, rchn); ++ double_evtchn_unlock(lchn, rchn, flags); + + bind->local_port = lport; + +@@ -387,6 +395,7 @@ int evtchn_bind_virq(evtchn_bind_virq_t + struct domain *d = current->domain; + int virq = bind->virq, vcpu = bind->vcpu; + int rc = 0; ++ unsigned long flags; + + if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) + return -EINVAL; +@@ -424,14 +433,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t + + chn = evtchn_from_port(d, port); + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_VIRQ; + chn->notify_vcpu_id = vcpu; + chn->u.virq = virq; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + v->virq_to_evtchn[virq] = bind->port = port; + +@@ -448,6 +457,7 @@ static long evtchn_bind_ipi(evtchn_bind_ + struct domain *d = current->domain; + int port, vcpu = bind->vcpu; + long rc = 0; ++ unsigned long flags; + + if ( domain_vcpu(d, vcpu) == NULL ) + return -ENOENT; +@@ -459,13 +469,13 @@ static long evtchn_bind_ipi(evtchn_bind_ + + chn = evtchn_from_port(d, port); + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_IPI; + chn->notify_vcpu_id = vcpu; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + bind->port = port; + +@@ -509,6 +519,7 @@ static long evtchn_bind_pirq(evtchn_bind + struct pirq *info; + int port = 0, pirq = bind->pirq; + long rc; ++ unsigned long flags; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) + return -EINVAL; +@@ -541,14 +552,14 @@ static long evtchn_bind_pirq(evtchn_bind + goto out; + } + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_PIRQ; + chn->u.pirq.irq = pirq; + link_pirq_port(port, chn, v); + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + bind->port = port; + +@@ -569,6 +580,7 @@ int evtchn_close(struct domain *d1, int + struct evtchn *chn1, *chn2; + int port2; + long rc = 0; ++ unsigned long flags; + + again: + spin_lock(&d1->event_lock); +@@ -668,14 +680,14 @@ int evtchn_close(struct domain *d1, int + BUG_ON(chn2->state != ECS_INTERDOMAIN); + BUG_ON(chn2->u.interdomain.remote_dom != d1); + +- double_evtchn_lock(chn1, chn2); ++ flags = double_evtchn_lock(chn1, chn2); + + evtchn_free(d1, chn1); + + chn2->state = ECS_UNBOUND; + chn2->u.unbound.remote_domid = d1->domain_id; + +- double_evtchn_unlock(chn1, chn2); ++ double_evtchn_unlock(chn1, chn2, flags); + + goto out; + +@@ -683,9 +695,9 @@ int evtchn_close(struct domain *d1, int + BUG(); + } + +- spin_lock(&chn1->lock); ++ spin_lock_irqsave(&chn1->lock, flags); + evtchn_free(d1, chn1); +- spin_unlock(&chn1->lock); ++ spin_unlock_irqrestore(&chn1->lock, flags); + + out: + if ( d2 != NULL ) +@@ -705,13 +717,14 @@ int evtchn_send(struct domain *ld, unsig + struct evtchn *lchn, *rchn; + struct domain *rd; + int rport, ret = 0; ++ unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + return -EINVAL; + + lchn = evtchn_from_port(ld, lport); + +- spin_lock(&lchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); + + /* Guest cannot send via a Xen-attached event channel. */ + if ( unlikely(consumer_is_xen(lchn)) ) +@@ -746,7 +759,7 @@ int evtchn_send(struct domain *ld, unsig + } + + out: +- spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&lchn->lock, flags); + + return ret; + } +@@ -1238,6 +1251,7 @@ int alloc_unbound_xen_event_channel( + { + struct evtchn *chn; + int port, rc; ++ unsigned long flags; + + spin_lock(&ld->event_lock); + +@@ -1250,14 +1264,14 @@ int alloc_unbound_xen_event_channel( + if ( rc ) + goto out; + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_UNBOUND; + chn->xen_consumer = get_xen_consumer(notification_fn); + chn->notify_vcpu_id = lvcpu; + chn->u.unbound.remote_domid = remote_domid; + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + +@@ -1280,11 +1294,12 @@ void notify_via_xen_event_channel(struct + { + struct evtchn *lchn, *rchn; + struct domain *rd; ++ unsigned long flags; + + ASSERT(port_is_valid(ld, lport)); + lchn = evtchn_from_port(ld, lport); + +- spin_lock(&lchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); + + if ( likely(lchn->state == ECS_INTERDOMAIN) ) + { +@@ -1294,7 +1309,7 @@ void notify_via_xen_event_channel(struct + evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); + } + +- spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&lchn->lock, flags); + } + + void evtchn_check_pollers(struct domain *d, unsigned int port) diff --git a/xsa343-4.12-3.patch b/xsa343-4.12-3.patch new file mode 100644 index 0000000..fb1751d --- /dev/null +++ b/xsa343-4.12-3.patch @@ -0,0 +1,381 @@ +From: Jan Beulich +Subject: evtchn: address races with evtchn_reset() + +Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely +lock-less manner, as both may change by a racing evtchn_reset(). In the +common case, at least one of the domain's event lock or the per-channel +lock needs to be held. In the specific case of the inter-domain sending +by evtchn_send() and notify_via_xen_event_channel() holding the other +side's per-channel lock is sufficient, as the channel can't change state +without both per-channel locks held. Without such a channel changing +state, evtchn_reset() can't complete successfully. + +Lock-free accesses continue to be permitted for the shim (calling some +otherwise internal event channel functions), as this happens while the +domain is in effectively single-threaded mode. Special care also needs +taking for the shim's marking of in-use ports as ECS_RESERVED (allowing +use of such ports in the shim case is okay because switching into and +hence also out of FIFO mode is impossible there). + +As a side effect, certain operations on Xen bound event channels which +were mistakenly permitted so far (e.g. unmask or poll) will be refused +now. + +This is part of XSA-343. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall + +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2364,14 +2364,24 @@ static void dump_irqs(unsigned char key) + + for ( i = 0; i < action->nr_guests; i++ ) + { ++ struct evtchn *evtchn; ++ unsigned int pending = 2, masked = 2; ++ + d = action->guest[i]; + pirq = domain_irq_to_pirq(d, irq); + info = pirq_info(d, pirq); ++ evtchn = evtchn_from_port(d, info->evtchn); ++ local_irq_disable(); ++ if ( spin_trylock(&evtchn->lock) ) ++ { ++ pending = evtchn_is_pending(d, evtchn); ++ masked = evtchn_is_masked(d, evtchn); ++ spin_unlock(&evtchn->lock); ++ } ++ local_irq_enable(); + printk("%u:%3d(%c%c%c)", +- d->domain_id, pirq, +- evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', +- evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', +- (info->masked ? 'M' : '-')); ++ d->domain_id, pirq, "-P?"[pending], ++ "-M?"[masked], info->masked ? 'M' : '-'); + if ( i != action->nr_guests ) + printk(","); + } +--- a/xen/arch/x86/pv/shim.c ++++ b/xen/arch/x86/pv/shim.c +@@ -662,8 +662,11 @@ void pv_shim_inject_evtchn(unsigned int + if ( port_is_valid(guest, port) ) + { + struct evtchn *chn = evtchn_from_port(guest, port); ++ unsigned long flags; + ++ spin_lock_irqsave(&chn->lock, flags); + evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); ++ spin_unlock_irqrestore(&chn->lock, flags); + } + } + +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -63,8 +63,10 @@ static void evtchn_2l_unmask(struct doma + } + } + +-static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) ++static bool evtchn_2l_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { ++ evtchn_port_t port = evtchn->port; + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +@@ -72,8 +74,10 @@ static bool evtchn_2l_is_pending(const s + guest_test_bit(d, port, &shared_info(d, evtchn_pending))); + } + +-static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) ++static bool evtchn_2l_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) + { ++ evtchn_port_t port = evtchn->port; + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -156,8 +156,9 @@ int evtchn_allocate_port(struct domain * + + if ( port_is_valid(d, port) ) + { +- if ( evtchn_from_port(d, port)->state != ECS_FREE || +- evtchn_port_is_busy(d, port) ) ++ const struct evtchn *chn = evtchn_from_port(d, port); ++ ++ if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) + return -EBUSY; + } + else +@@ -774,6 +775,7 @@ void send_guest_vcpu_virq(struct vcpu *v + unsigned long flags; + int port; + struct domain *d; ++ struct evtchn *chn; + + ASSERT(!virq_is_global(virq)); + +@@ -784,7 +786,10 @@ void send_guest_vcpu_virq(struct vcpu *v + goto out; + + d = v->domain; +- evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); ++ chn = evtchn_from_port(d, port); ++ spin_lock(&chn->lock); ++ evtchn_port_set_pending(d, v->vcpu_id, chn); ++ spin_unlock(&chn->lock); + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -813,7 +818,9 @@ void send_guest_global_virq(struct domai + goto out; + + chn = evtchn_from_port(d, port); ++ spin_lock(&chn->lock); + evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ spin_unlock(&chn->lock); + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -823,6 +830,7 @@ void send_guest_pirq(struct domain *d, c + { + int port; + struct evtchn *chn; ++ unsigned long flags; + + /* + * PV guests: It should not be possible to race with __evtchn_close(). The +@@ -837,7 +845,9 @@ void send_guest_pirq(struct domain *d, c + } + + chn = evtchn_from_port(d, port); ++ spin_lock_irqsave(&chn->lock, flags); + evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ spin_unlock_irqrestore(&chn->lock, flags); + } + + static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; +@@ -1034,12 +1044,15 @@ int evtchn_unmask(unsigned int port) + { + struct domain *d = current->domain; + struct evtchn *evtchn; ++ unsigned long flags; + + if ( unlikely(!port_is_valid(d, port)) ) + return -EINVAL; + + evtchn = evtchn_from_port(d, port); ++ spin_lock_irqsave(&evtchn->lock, flags); + evtchn_port_unmask(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); + + return 0; + } +@@ -1449,8 +1462,8 @@ static void domain_dump_evtchn_info(stru + + printk(" %4u [%d/%d/", + port, +- evtchn_port_is_pending(d, port), +- evtchn_port_is_masked(d, port)); ++ evtchn_is_pending(d, chn), ++ evtchn_is_masked(d, chn)); + evtchn_port_print_state(d, chn); + printk("]: s=%d n=%d x=%d", + chn->state, chn->notify_vcpu_id, chn->xen_consumer); +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -296,23 +296,26 @@ static void evtchn_fifo_unmask(struct do + evtchn_fifo_set_pending(v, evtchn); + } + +-static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) ++static bool evtchn_fifo_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); + } + +-static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) ++static bool_t evtchn_fifo_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); + } + +-static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) ++static bool_t evtchn_fifo_is_busy(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); + } +--- a/xen/include/asm-x86/event.h ++++ b/xen/include/asm-x86/event.h +@@ -47,4 +47,10 @@ static inline bool arch_virq_is_global(u + return true; + } + ++#ifdef CONFIG_PV_SHIM ++# include ++# define arch_evtchn_is_special(chn) \ ++ (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) ++#endif ++ + #endif +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -133,6 +133,24 @@ static inline struct evtchn *evtchn_from + return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); + } + ++/* ++ * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be ++ * taken care of separately where used for Xen's internal purposes. ++ */ ++static bool evtchn_usable(const struct evtchn *evtchn) ++{ ++ if ( evtchn->xen_consumer ) ++ return false; ++ ++#ifdef arch_evtchn_is_special ++ if ( arch_evtchn_is_special(evtchn) ) ++ return true; ++#endif ++ ++ BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); ++ return evtchn->state > ECS_RESERVED; ++} ++ + /* Wait on a Xen-attached event channel. */ + #define wait_on_xen_event_channel(port, condition) \ + do { \ +@@ -165,19 +183,24 @@ int evtchn_reset(struct domain *d); + + /* + * Low-level event channel port ops. ++ * ++ * All hooks have to be called with a lock held which prevents the channel ++ * from changing state. This may be the domain event lock, the per-channel ++ * lock, or in the case of sending interdomain events also the other side's ++ * per-channel lock. Exceptions apply in certain cases for the PV shim. + */ + struct evtchn_port_ops { + void (*init)(struct domain *d, struct evtchn *evtchn); + void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); + void (*clear_pending)(struct domain *d, struct evtchn *evtchn); + void (*unmask)(struct domain *d, struct evtchn *evtchn); +- bool (*is_pending)(const struct domain *d, evtchn_port_t port); +- bool (*is_masked)(const struct domain *d, evtchn_port_t port); ++ bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); ++ bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); + /* + * Is the port unavailable because it's still being cleaned up + * after being closed? + */ +- bool (*is_busy)(const struct domain *d, evtchn_port_t port); ++ bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); + int (*set_priority)(struct domain *d, struct evtchn *evtchn, + unsigned int priority); + void (*print_state)(struct domain *d, const struct evtchn *evtchn); +@@ -193,38 +216,67 @@ static inline void evtchn_port_set_pendi + unsigned int vcpu_id, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); + } + + static inline void evtchn_port_clear_pending(struct domain *d, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->clear_pending(d, evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->clear_pending(d, evtchn); + } + + static inline void evtchn_port_unmask(struct domain *d, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->unmask(d, evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->unmask(d, evtchn); + } + +-static inline bool evtchn_port_is_pending(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { +- return d->evtchn_port_ops->is_pending(d, port); ++ return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); + } + +-static inline bool evtchn_port_is_masked(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) + { +- return d->evtchn_port_ops->is_masked(d, port); ++ struct evtchn *evtchn = evtchn_from_port(d, port); ++ bool rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&evtchn->lock, flags); ++ rc = evtchn_is_pending(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ return rc; ++} ++ ++static inline bool evtchn_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) ++{ ++ return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); ++} ++ ++static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) ++{ ++ struct evtchn *evtchn = evtchn_from_port(d, port); ++ bool rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&evtchn->lock, flags); ++ rc = evtchn_is_masked(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ return rc; + } + +-static inline bool evtchn_port_is_busy(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_is_busy(const struct domain *d, ++ const struct evtchn *evtchn) + { + return d->evtchn_port_ops->is_busy && +- d->evtchn_port_ops->is_busy(d, port); ++ d->evtchn_port_ops->is_busy(d, evtchn); + } + + static inline int evtchn_port_set_priority(struct domain *d, +@@ -233,6 +285,8 @@ static inline int evtchn_port_set_priori + { + if ( !d->evtchn_port_ops->set_priority ) + return -ENOSYS; ++ if ( !evtchn_usable(evtchn) ) ++ return -EACCES; + return d->evtchn_port_ops->set_priority(d, evtchn, priority); + } + diff --git a/xsa344-4.12-1.patch b/xsa344-4.12-1.patch new file mode 100644 index 0000000..ef78c83 --- /dev/null +++ b/xsa344-4.12-1.patch @@ -0,0 +1,132 @@ +From: Jan Beulich +Subject: evtchn: arrange for preemption in evtchn_destroy() + +Especially closing of fully established interdomain channels can take +quite some time, due to the locking involved. Therefore we shouldn't +assume we can clean up still active ports all in one go. Besides adding +the necessary preemption check, also avoid pointlessly starting from +(or now really ending at) 0; 1 is the lowest numbered port which may +need closing. + +Since we're now reducing ->valid_evtchns, free_xen_event_channel(), +and (at least to be on the safe side) notify_via_xen_event_channel() +need to cope with attempts to close / unbind from / send through already +closed (and no longer valid, as per port_is_valid()) ports. + +This is part of XSA-344. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -724,7 +724,6 @@ int domain_kill(struct domain *d) + return domain_kill(d); + d->is_dying = DOMDYING_dying; + argo_destroy(d); +- evtchn_destroy(d); + gnttab_release_mappings(d); + tmem_destroy(d->tmem_client); + vnuma_destroy(d->vnuma); +@@ -732,6 +731,9 @@ int domain_kill(struct domain *d) + d->tmem_client = NULL; + /* fallthrough */ + case DOMDYING_dying: ++ rc = evtchn_destroy(d); ++ if ( rc ) ++ break; + rc = domain_relinquish_resources(d); + if ( rc != 0 ) + break; +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -1297,7 +1297,16 @@ int alloc_unbound_xen_event_channel( + + void free_xen_event_channel(struct domain *d, int port) + { +- BUG_ON(!port_is_valid(d, port)); ++ if ( !port_is_valid(d, port) ) ++ { ++ /* ++ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing ++ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). ++ */ ++ smp_rmb(); ++ BUG_ON(!d->is_dying); ++ return; ++ } + + evtchn_close(d, port, 0); + } +@@ -1309,7 +1318,17 @@ void notify_via_xen_event_channel(struct + struct domain *rd; + unsigned long flags; + +- ASSERT(port_is_valid(ld, lport)); ++ if ( !port_is_valid(ld, lport) ) ++ { ++ /* ++ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing ++ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). ++ */ ++ smp_rmb(); ++ ASSERT(ld->is_dying); ++ return; ++ } ++ + lchn = evtchn_from_port(ld, lport); + + spin_lock_irqsave(&lchn->lock, flags); +@@ -1380,8 +1399,7 @@ int evtchn_init(struct domain *d, unsign + return 0; + } + +- +-void evtchn_destroy(struct domain *d) ++int evtchn_destroy(struct domain *d) + { + unsigned int i; + +@@ -1390,14 +1408,29 @@ void evtchn_destroy(struct domain *d) + spin_barrier(&d->event_lock); + + /* Close all existing event channels. */ +- for ( i = 0; port_is_valid(d, i); i++ ) ++ for ( i = d->valid_evtchns; --i; ) ++ { + evtchn_close(d, i, 0); + ++ /* ++ * Avoid preempting when called from domain_create()'s error path, ++ * and don't check too often (choice of frequency is arbitrary). ++ */ ++ if ( i && !(i & 0x3f) && d->is_dying != DOMDYING_dead && ++ hypercall_preempt_check() ) ++ { ++ write_atomic(&d->valid_evtchns, i); ++ return -ERESTART; ++ } ++ } ++ + ASSERT(!d->active_evtchns); + + clear_global_virq_handlers(d); + + evtchn_fifo_destroy(d); ++ ++ return 0; + } + + +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -136,7 +136,7 @@ struct evtchn + } __attribute__((aligned(64))); + + int evtchn_init(struct domain *d, unsigned int max_port); +-void evtchn_destroy(struct domain *d); /* from domain_kill */ ++int evtchn_destroy(struct domain *d); /* from domain_kill */ + void evtchn_destroy_final(struct domain *d); /* from complete_domain_destroy */ + + struct waitqueue_vcpu; diff --git a/xsa344-4.12-2.patch b/xsa344-4.12-2.patch new file mode 100644 index 0000000..eda4cac --- /dev/null +++ b/xsa344-4.12-2.patch @@ -0,0 +1,203 @@ +From: Jan Beulich +Subject: evtchn: arrange for preemption in evtchn_reset() + +Like for evtchn_destroy() looping over all possible event channels to +close them can take a significant amount of time. Unlike done there, we +can't alter domain properties (i.e. d->valid_evtchns) here. Borrow, in a +lightweight form, the paging domctl continuation concept, redirecting +the continuations to different sub-ops. Just like there this is to be +able to allow for predictable overall results of the involved sub-ops: +Racing requests should either complete or be refused. + +Note that a domain can't interfere with an already started (by a remote +domain) reset, due to being paused. It can prevent a remote reset from +happening by leaving a reset unfinished, but that's only going to affect +itself. + +This is part of XSA-344. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1170,7 +1170,7 @@ void domain_unpause_except_self(struct d + domain_unpause(d); + } + +-int domain_soft_reset(struct domain *d) ++int domain_soft_reset(struct domain *d, bool resuming) + { + struct vcpu *v; + int rc; +@@ -1184,7 +1184,7 @@ int domain_soft_reset(struct domain *d) + } + spin_unlock(&d->shutdown_lock); + +- rc = evtchn_reset(d); ++ rc = evtchn_reset(d, resuming); + if ( rc ) + return rc; + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -585,12 +585,22 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + + case XEN_DOMCTL_soft_reset: ++ case XEN_DOMCTL_soft_reset_cont: + if ( d == current->domain ) /* no domain_pause() */ + { + ret = -EINVAL; + break; + } +- ret = domain_soft_reset(d); ++ ret = domain_soft_reset(d, op->cmd == XEN_DOMCTL_soft_reset_cont); ++ if ( ret == -ERESTART ) ++ { ++ op->cmd = XEN_DOMCTL_soft_reset_cont; ++ if ( !__copy_field_to_guest(u_domctl, op, cmd) ) ++ ret = hypercall_create_continuation(__HYPERVISOR_domctl, ++ "h", u_domctl); ++ else ++ ret = -EFAULT; ++ } + break; + + case XEN_DOMCTL_destroydomain: +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -1057,7 +1057,7 @@ int evtchn_unmask(unsigned int port) + return 0; + } + +-int evtchn_reset(struct domain *d) ++int evtchn_reset(struct domain *d, bool resuming) + { + unsigned int i; + int rc = 0; +@@ -1065,11 +1065,40 @@ int evtchn_reset(struct domain *d) + if ( d != current->domain && !d->controller_pause_count ) + return -EINVAL; + +- for ( i = 0; port_is_valid(d, i); i++ ) ++ spin_lock(&d->event_lock); ++ ++ /* ++ * If we are resuming, then start where we stopped. Otherwise, check ++ * that a reset operation is not already in progress, and if none is, ++ * record that this is now the case. ++ */ ++ i = resuming ? d->next_evtchn : !d->next_evtchn; ++ if ( i > d->next_evtchn ) ++ d->next_evtchn = i; ++ ++ spin_unlock(&d->event_lock); ++ ++ if ( !i ) ++ return -EBUSY; ++ ++ for ( ; port_is_valid(d, i); i++ ) ++ { + evtchn_close(d, i, 1); + ++ /* NB: Choice of frequency is arbitrary. */ ++ if ( !(i & 0x3f) && hypercall_preempt_check() ) ++ { ++ spin_lock(&d->event_lock); ++ d->next_evtchn = i; ++ spin_unlock(&d->event_lock); ++ return -ERESTART; ++ } ++ } ++ + spin_lock(&d->event_lock); + ++ d->next_evtchn = 0; ++ + if ( d->active_evtchns > d->xen_evtchns ) + rc = -EAGAIN; + else if ( d->evtchn_fifo ) +@@ -1204,7 +1233,8 @@ long do_event_channel_op(int cmd, XEN_GU + break; + } + +- case EVTCHNOP_reset: { ++ case EVTCHNOP_reset: ++ case EVTCHNOP_reset_cont: { + struct evtchn_reset reset; + struct domain *d; + +@@ -1217,9 +1247,13 @@ long do_event_channel_op(int cmd, XEN_GU + + rc = xsm_evtchn_reset(XSM_TARGET, current->domain, d); + if ( !rc ) +- rc = evtchn_reset(d); ++ rc = evtchn_reset(d, cmd == EVTCHNOP_reset_cont); + + rcu_unlock_domain(d); ++ ++ if ( rc == -ERESTART ) ++ rc = hypercall_create_continuation(__HYPERVISOR_event_channel_op, ++ "ih", EVTCHNOP_reset_cont, arg); + break; + } + +--- a/xen/include/public/domctl.h ++++ b/xen/include/public/domctl.h +@@ -1144,7 +1144,10 @@ struct xen_domctl { + #define XEN_DOMCTL_iomem_permission 20 + #define XEN_DOMCTL_ioport_permission 21 + #define XEN_DOMCTL_hypercall_init 22 +-#define XEN_DOMCTL_arch_setup 23 /* Obsolete IA64 only */ ++#ifdef __XEN__ ++/* #define XEN_DOMCTL_arch_setup 23 Obsolete IA64 only */ ++#define XEN_DOMCTL_soft_reset_cont 23 ++#endif + #define XEN_DOMCTL_settimeoffset 24 + #define XEN_DOMCTL_getvcpuaffinity 25 + #define XEN_DOMCTL_real_mode_area 26 /* Obsolete PPC only */ +--- a/xen/include/public/event_channel.h ++++ b/xen/include/public/event_channel.h +@@ -74,6 +74,9 @@ + #define EVTCHNOP_init_control 11 + #define EVTCHNOP_expand_array 12 + #define EVTCHNOP_set_priority 13 ++#ifdef __XEN__ ++#define EVTCHNOP_reset_cont 14 ++#endif + /* ` } */ + + typedef uint32_t evtchn_port_t; +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -171,7 +171,7 @@ void evtchn_check_pollers(struct domain + void evtchn_2l_init(struct domain *d); + + /* Close all event channels and reset to 2-level ABI. */ +-int evtchn_reset(struct domain *d); ++int evtchn_reset(struct domain *d, bool resuming); + + /* + * Low-level event channel port ops. +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -356,6 +356,8 @@ struct domain + * EVTCHNOP_reset). Read/write access like for active_evtchns. + */ + unsigned int xen_evtchns; ++ /* Port to resume from in evtchn_reset(), when in a continuation. */ ++ unsigned int next_evtchn; + spinlock_t event_lock; + const struct evtchn_port_ops *evtchn_port_ops; + struct evtchn_fifo_domain *evtchn_fifo; +@@ -628,7 +630,7 @@ int domain_shutdown(struct domain *d, u8 + void domain_resume(struct domain *d); + void domain_pause_for_debugger(void); + +-int domain_soft_reset(struct domain *d); ++int domain_soft_reset(struct domain *d, bool resuming); + + int vcpu_start_shutdown_deferral(struct vcpu *v); + void vcpu_end_shutdown_deferral(struct vcpu *v); From 47e68f69a5beeee412026226bddd322c1a629ec9 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 20 Oct 2020 20:47:19 +0100 Subject: [PATCH 09/11] 3 security updates x86: Race condition in Xen mapping code [XSA-345] undue deferral of IOMMU TLB flushes [XSA-346] unsafe AMD IOMMU page table updates [XSA-347] --- xen.spec | 21 +- ...map_pages_to_xen-to-have-only-a-sing.patch | 93 +++++++ ...modify_xen_mappings-to-have-one-exit.patch | 67 +++++ ...ome-races-in-hypervisor-mapping-upda.patch | 248 ++++++++++++++++++ xsa346-4.12-1.patch | 50 ++++ xsa346-4.12-2.patch | 202 ++++++++++++++ xsa347-4.12-1.patch | 52 ++++ xsa347-4.12-2.patch | 80 ++++++ 8 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 xsa345-4.12-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch create mode 100644 xsa345-4.12-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch create mode 100644 xsa345-4.12-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch create mode 100644 xsa346-4.12-1.patch create mode 100644 xsa346-4.12-2.patch create mode 100644 xsa347-4.12-1.patch create mode 100644 xsa347-4.12-2.patch diff --git a/xen.spec b/xen.spec index f408fb2..6192b88 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.3 -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 @@ -145,6 +145,13 @@ Patch81: xsa343-4.12-2.patch Patch82: xsa343-4.12-3.patch Patch83: xsa344-4.12-1.patch Patch84: xsa344-4.12-2.patch +Patch85: xsa345-4.12-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch +Patch86: xsa345-4.12-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch +Patch87: xsa345-4.12-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch +Patch88: xsa346-4.12-1.patch +Patch89: xsa346-4.12-2.patch +Patch90: xsa347-4.12-1.patch +Patch91: xsa347-4.12-2.patch %if %build_qemutrad @@ -372,6 +379,13 @@ manage Xen virtual machines. %patch82 -p1 %patch83 -p1 %patch84 -p1 +%patch85 -p1 +%patch86 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -936,6 +950,11 @@ fi %endif %changelog +* Tue Oct 20 2020 Michael Young - 4.12.3-6 +- x86: Race condition in Xen mapping code [XSA-345] +- undue deferral of IOMMU TLB flushes [XSA-346] +- unsafe AMD IOMMU page table updates [XSA-347] + * Tue Sep 22 2020 Michael Young - 4.12.3-5 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) diff --git a/xsa345-4.12-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch b/xsa345-4.12-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch new file mode 100644 index 0000000..ea243f3 --- /dev/null +++ b/xsa345-4.12-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch @@ -0,0 +1,93 @@ +From e33fad3044aaaeec6ed9914925d9558695bdb09d Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Sat, 11 Jan 2020 21:57:41 +0000 +Subject: [PATCH 1/3] x86/mm: Refactor map_pages_to_xen to have only a single + exit path + +We will soon need to perform clean-ups before returning. + +No functional change. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Wei Liu +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +--- + xen/arch/x86/mm.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index b4c90bd054..0e540f143b 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5227,6 +5227,7 @@ int map_pages_to_xen( + l2_pgentry_t *pl2e, ol2e; + l1_pgentry_t *pl1e, ol1e; + unsigned int i; ++ int rc = -ENOMEM; + + #define flush_flags(oldf) do { \ + unsigned int o_ = (oldf); \ +@@ -5247,7 +5248,8 @@ int map_pages_to_xen( + l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); + + if ( !pl3e ) +- return -ENOMEM; ++ goto out; ++ + ol3e = *pl3e; + + if ( cpu_has_page1gb && +@@ -5335,7 +5337,7 @@ int map_pages_to_xen( + + pl2e = alloc_xen_pagetable(); + if ( pl2e == NULL ) +- return -ENOMEM; ++ goto out; + + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + l2e_write(pl2e + i, +@@ -5364,7 +5366,7 @@ int map_pages_to_xen( + + pl2e = virt_to_xen_l2e(virt); + if ( !pl2e ) +- return -ENOMEM; ++ goto out; + + if ( ((((virt >> PAGE_SHIFT) | mfn_x(mfn)) & + ((1u << PAGETABLE_ORDER) - 1)) == 0) && +@@ -5407,7 +5409,7 @@ int map_pages_to_xen( + { + pl1e = virt_to_xen_l1e(virt); + if ( pl1e == NULL ) +- return -ENOMEM; ++ goto out; + } + else if ( l2e_get_flags(*pl2e) & _PAGE_PSE ) + { +@@ -5434,7 +5436,7 @@ int map_pages_to_xen( + + pl1e = alloc_xen_pagetable(); + if ( pl1e == NULL ) +- return -ENOMEM; ++ goto out; + + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + l1e_write(&pl1e[i], +@@ -5578,7 +5580,10 @@ int map_pages_to_xen( + + #undef flush_flags + +- return 0; ++ rc = 0; ++ ++ out: ++ return rc; + } + + int populate_pt_range(unsigned long virt, unsigned long nr_mfns) +-- +2.25.1 + diff --git a/xsa345-4.12-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch b/xsa345-4.12-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch new file mode 100644 index 0000000..f9dec3e --- /dev/null +++ b/xsa345-4.12-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch @@ -0,0 +1,67 @@ +From a151b07d504d7f442256e1c82917334216c58a21 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Sat, 11 Jan 2020 21:57:42 +0000 +Subject: [PATCH 2/3] x86/mm: Refactor modify_xen_mappings to have one exit + path + +We will soon need to perform clean-ups before returning. + +No functional change. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Wei Liu +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +--- + xen/arch/x86/mm.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 0e540f143b..bff2689e60 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5610,6 +5610,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + l1_pgentry_t *pl1e; + unsigned int i; + unsigned long v = s; ++ int rc = -ENOMEM; + + /* Set of valid PTE bits which may be altered. */ + #define FLAGS_MASK (_PAGE_NX|_PAGE_RW|_PAGE_PRESENT) +@@ -5651,7 +5652,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + /* PAGE1GB: shatter the superpage and fall through. */ + pl2e = alloc_xen_pagetable(); + if ( !pl2e ) +- return -ENOMEM; ++ goto out; ++ + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + l2e_write(pl2e + i, + l2e_from_pfn(l3e_get_pfn(*pl3e) + +@@ -5706,7 +5708,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + /* PSE: shatter the superpage and try again. */ + pl1e = alloc_xen_pagetable(); + if ( !pl1e ) +- return -ENOMEM; ++ goto out; ++ + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + l1e_write(&pl1e[i], + l1e_from_pfn(l2e_get_pfn(*pl2e) + i, +@@ -5835,7 +5838,10 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + flush_area(NULL, FLUSH_TLB_GLOBAL); + + #undef FLAGS_MASK +- return 0; ++ rc = 0; ++ ++ out: ++ return rc; + } + + #undef flush_area +-- +2.25.1 + diff --git a/xsa345-4.12-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch b/xsa345-4.12-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch new file mode 100644 index 0000000..1db4c49 --- /dev/null +++ b/xsa345-4.12-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch @@ -0,0 +1,248 @@ +From abaf05e183f3bc3927844ae16d6642a382c0dbef Mon Sep 17 00:00:00 2001 +From: Hongyan Xia +Date: Sat, 11 Jan 2020 21:57:43 +0000 +Subject: [PATCH 3/3] x86/mm: Prevent some races in hypervisor mapping updates + +map_pages_to_xen will attempt to coalesce mappings into 2MiB and 1GiB +superpages if possible, to maximize TLB efficiency. This means both +replacing superpage entries with smaller entries, and replacing +smaller entries with superpages. + +Unfortunately, while some potential races are handled correctly, +others are not. These include: + +1. When one processor modifies a sub-superpage mapping while another +processor replaces the entire range with a superpage. + +Take the following example: + +Suppose L3[N] points to L2. And suppose we have two processors, A and +B. + +* A walks the pagetables, get a pointer to L2. +* B replaces L3[N] with a 1GiB mapping. +* B Frees L2 +* A writes L2[M] # + +This is race exacerbated by the fact that virt_to_xen_l[21]e doesn't +handle higher-level superpages properly: If you call virt_xen_to_l2e +on a virtual address within an L3 superpage, you'll either hit a BUG() +(most likely), or get a pointer into the middle of a data page; same +with virt_xen_to_l1 on a virtual address within either an L3 or L2 +superpage. + +So take the following example: + +* A reads pl3e and discovers it to point to an L2. +* B replaces L3[N] with a 1GiB mapping +* A calls virt_to_xen_l2e() and hits the BUG_ON() # + +2. When two processors simultaneously try to replace a sub-superpage +mapping with a superpage mapping. + +Take the following example: + +Suppose L3[N] points to L2. And suppose we have two processors, A and B, +both trying to replace L3[N] with a superpage. + +* A walks the pagetables, get a pointer to pl3e, and takes a copy ol3e pointing to L2. +* B walks the pagetables, gets a pointre to pl3e, and takes a copy ol3e pointing to L2. +* A writes the new value into L3[N] +* B writes the new value into L3[N] +* A recursively frees all the L1's under L2, then frees L2 +* B recursively double-frees all the L1's under L2, then double-frees L2 # + +Fix this by grabbing a lock for the entirety of the mapping update +operation. + +Rather than grabbing map_pgdir_lock for the entire operation, however, +repurpose the PGT_locked bit from L3's page->type_info as a lock. +This means that rather than locking the entire address space, we +"only" lock a single 512GiB chunk of hypervisor address space at a +time. + +There was a proposal for a lock-and-reverify approach, where we walk +the pagetables to the point where we decide what to do; then grab the +map_pgdir_lock, re-verify the information we collected without the +lock, and finally make the change (starting over again if anything had +changed). Without being able to guarantee that the L2 table wasn't +freed, however, that means every read would need to be considered +potentially unsafe. Thinking carefully about that is probably +something that wants to be done on public, not under time pressure. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +--- + xen/arch/x86/mm.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 89 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index bff2689e60..d6ba8c4bb4 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -2197,6 +2197,50 @@ void page_unlock(struct page_info *page) + current_locked_page_set(NULL); + } + ++/* ++ * L3 table locks: ++ * ++ * Used for serialization in map_pages_to_xen() and modify_xen_mappings(). ++ * ++ * For Xen PT pages, the page->u.inuse.type_info is unused and it is safe to ++ * reuse the PGT_locked flag. This lock is taken only when we move down to L3 ++ * tables and below, since L4 (and above, for 5-level paging) is still globally ++ * protected by map_pgdir_lock. ++ * ++ * PV MMU update hypercalls call map_pages_to_xen while holding a page's page_lock(). ++ * This has two implications: ++ * - We cannot reuse reuse current_locked_page_* for debugging ++ * - To avoid the chance of deadlock, even for different pages, we ++ * must never grab page_lock() after grabbing l3t_lock(). This ++ * includes any page_lock()-based locks, such as ++ * mem_sharing_page_lock(). ++ * ++ * Also note that we grab the map_pgdir_lock while holding the ++ * l3t_lock(), so to avoid deadlock we must avoid grabbing them in ++ * reverse order. ++ */ ++static void l3t_lock(struct page_info *page) ++{ ++ unsigned long x, nx; ++ ++ do { ++ while ( (x = page->u.inuse.type_info) & PGT_locked ) ++ cpu_relax(); ++ nx = x | PGT_locked; ++ } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); ++} ++ ++static void l3t_unlock(struct page_info *page) ++{ ++ unsigned long x, nx, y = page->u.inuse.type_info; ++ ++ do { ++ x = y; ++ BUG_ON(!(x & PGT_locked)); ++ nx = x & ~PGT_locked; ++ } while ( (y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x ); ++} ++ + #ifdef CONFIG_PV + /* + * PTE flags that a guest may change without re-validating the PTE. +@@ -5217,6 +5261,23 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) + flush_area_local((const void *)v, f) : \ + flush_area_all((const void *)v, f)) + ++#define L3T_INIT(page) (page) = ZERO_BLOCK_PTR ++ ++#define L3T_LOCK(page) \ ++ do { \ ++ if ( locking ) \ ++ l3t_lock(page); \ ++ } while ( false ) ++ ++#define L3T_UNLOCK(page) \ ++ do { \ ++ if ( locking && (page) != ZERO_BLOCK_PTR ) \ ++ { \ ++ l3t_unlock(page); \ ++ (page) = ZERO_BLOCK_PTR; \ ++ } \ ++ } while ( false ) ++ + int map_pages_to_xen( + unsigned long virt, + mfn_t mfn, +@@ -5228,6 +5289,7 @@ int map_pages_to_xen( + l1_pgentry_t *pl1e, ol1e; + unsigned int i; + int rc = -ENOMEM; ++ struct page_info *current_l3page; + + #define flush_flags(oldf) do { \ + unsigned int o_ = (oldf); \ +@@ -5243,13 +5305,20 @@ int map_pages_to_xen( + } \ + } while (0) + ++ L3T_INIT(current_l3page); ++ + while ( nr_mfns != 0 ) + { +- l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); ++ l3_pgentry_t *pl3e, ol3e; + ++ L3T_UNLOCK(current_l3page); ++ ++ pl3e = virt_to_xen_l3e(virt); + if ( !pl3e ) + goto out; + ++ current_l3page = virt_to_page(pl3e); ++ L3T_LOCK(current_l3page); + ol3e = *pl3e; + + if ( cpu_has_page1gb && +@@ -5583,6 +5652,7 @@ int map_pages_to_xen( + rc = 0; + + out: ++ L3T_UNLOCK(current_l3page); + return rc; + } + +@@ -5611,6 +5681,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + unsigned int i; + unsigned long v = s; + int rc = -ENOMEM; ++ struct page_info *current_l3page; + + /* Set of valid PTE bits which may be altered. */ + #define FLAGS_MASK (_PAGE_NX|_PAGE_RW|_PAGE_PRESENT) +@@ -5619,11 +5690,22 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + ASSERT(IS_ALIGNED(s, PAGE_SIZE)); + ASSERT(IS_ALIGNED(e, PAGE_SIZE)); + ++ L3T_INIT(current_l3page); ++ + while ( v < e ) + { +- l3_pgentry_t *pl3e = virt_to_xen_l3e(v); ++ l3_pgentry_t *pl3e; ++ ++ L3T_UNLOCK(current_l3page); + +- if ( !pl3e || !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) ++ pl3e = virt_to_xen_l3e(v); ++ if ( !pl3e ) ++ goto out; ++ ++ current_l3page = virt_to_page(pl3e); ++ L3T_LOCK(current_l3page); ++ ++ if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) + { + /* Confirm the caller isn't trying to create new mappings. */ + ASSERT(!(nf & _PAGE_PRESENT)); +@@ -5841,9 +5923,13 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + rc = 0; + + out: ++ L3T_UNLOCK(current_l3page); + return rc; + } + ++#undef L3T_LOCK ++#undef L3T_UNLOCK ++ + #undef flush_area + + int destroy_xen_mappings(unsigned long s, unsigned long e) +-- +2.25.1 + diff --git a/xsa346-4.12-1.patch b/xsa346-4.12-1.patch new file mode 100644 index 0000000..5fbbe80 --- /dev/null +++ b/xsa346-4.12-1.patch @@ -0,0 +1,50 @@ +From: Jan Beulich +Subject: IOMMU: suppress "iommu_dont_flush_iotlb" when about to free a page + +Deferring flushes to a single, wide range one - as is done when +handling XENMAPSPACE_gmfn_range - is okay only as long as +pages don't get freed ahead of the eventual flush. While the only +function setting the flag (xenmem_add_to_physmap()) suggests by its name +that it's only mapping new entries, in reality the way +xenmem_add_to_physmap_one() works means an unmap would happen not only +for the page being moved (but not freed) but, if the destination GFN is +populated, also for the page being displaced from that GFN. Collapsing +the two flushes for this GFN into just one (end even more so deferring +it to a batched invocation) is not correct. + +This is part of XSA-346. + +Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Acked-by: Julien Grall + +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -300,6 +300,7 @@ int guest_remove_page(struct domain *d, + p2m_type_t p2mt; + #endif + mfn_t mfn; ++ bool *dont_flush_p, dont_flush; + int rc; + + #ifdef CONFIG_X86 +@@ -386,8 +387,18 @@ int guest_remove_page(struct domain *d, + return -ENXIO; + } + ++ /* ++ * Since we're likely to free the page below, we need to suspend ++ * xenmem_add_to_physmap()'s suppressing of IOMMU TLB flushes. ++ */ ++ dont_flush_p = &this_cpu(iommu_dont_flush_iotlb); ++ dont_flush = *dont_flush_p; ++ *dont_flush_p = false; ++ + rc = guest_physmap_remove_page(d, _gfn(gmfn), mfn, 0); + ++ *dont_flush_p = dont_flush; ++ + /* + * With the lack of an IOMMU on some platforms, domains with DMA-capable + * device must retrieve the same pfn when the hypercall populate_physmap diff --git a/xsa346-4.12-2.patch b/xsa346-4.12-2.patch new file mode 100644 index 0000000..d1be088 --- /dev/null +++ b/xsa346-4.12-2.patch @@ -0,0 +1,202 @@ +From: Jan Beulich +Subject: IOMMU: hold page ref until after deferred TLB flush + +When moving around a page via XENMAPSPACE_gmfn_range, deferring the TLB +flush for the "from" GFN range requires that the page remains allocated +to the guest until the TLB flush has actually occurred. Otherwise a +parallel hypercall to remove the page would only flush the TLB for the +GFN it has been moved to, but not the one is was mapped at originally. + +This is part of XSA-346. + +Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall + +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1211,7 +1211,7 @@ void share_xen_page_with_guest(struct pa + int xenmem_add_to_physmap_one( + struct domain *d, + unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, + gfn_t gfn) + { +@@ -1284,10 +1284,6 @@ int xenmem_add_to_physmap_one( + break; + } + case XENMAPSPACE_dev_mmio: +- /* extra should be 0. Reserved for future use. */ +- if ( extra.res0 ) +- return -EOPNOTSUPP; +- + rc = map_dev_mmio_region(d, gfn, 1, _mfn(idx)); + return rc; + +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4653,7 +4653,7 @@ static int handle_iomem_range(unsigned l + int xenmem_add_to_physmap_one( + struct domain *d, + unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, + gfn_t gpfn) + { +@@ -4740,9 +4740,20 @@ int xenmem_add_to_physmap_one( + rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); + + put_both: +- /* In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. */ ++ /* ++ * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. ++ * We also may need to transfer ownership of the page reference to our ++ * caller. ++ */ + if ( space == XENMAPSPACE_gmfn ) ++ { + put_gfn(d, gfn); ++ if ( !rc && extra.ppage ) ++ { ++ *extra.ppage = page; ++ page = NULL; ++ } ++ } + + if ( page ) + put_page(page); +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -824,11 +824,10 @@ int xenmem_add_to_physmap(struct domain + { + unsigned int done = 0; + long rc = 0; +- union xen_add_to_physmap_batch_extra extra; ++ union add_to_physmap_extra extra = {}; ++ struct page_info *pages[16]; + +- if ( xatp->space != XENMAPSPACE_gmfn_foreign ) +- extra.res0 = 0; +- else ++ if ( xatp->space == XENMAPSPACE_gmfn_foreign ) + extra.foreign_domid = DOMID_INVALID; + + if ( xatp->space != XENMAPSPACE_gmfn_range ) +@@ -843,7 +842,10 @@ int xenmem_add_to_physmap(struct domain + xatp->size -= start; + + if ( has_iommu_pt(d) ) ++ { + this_cpu(iommu_dont_flush_iotlb) = 1; ++ extra.ppage = &pages[0]; ++ } + + while ( xatp->size > done ) + { +@@ -855,8 +857,12 @@ int xenmem_add_to_physmap(struct domain + xatp->idx++; + xatp->gpfn++; + ++ if ( extra.ppage ) ++ ++extra.ppage; ++ + /* Check for continuation if it's not the last iteration. */ +- if ( xatp->size > ++done && hypercall_preempt_check() ) ++ if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || ++ (xatp->size > done && hypercall_preempt_check()) ) + { + rc = start + done; + break; +@@ -866,6 +872,7 @@ int xenmem_add_to_physmap(struct domain + if ( has_iommu_pt(d) ) + { + int ret; ++ unsigned int i; + + this_cpu(iommu_dont_flush_iotlb) = 0; + +@@ -874,6 +881,15 @@ int xenmem_add_to_physmap(struct domain + if ( unlikely(ret) && rc >= 0 ) + rc = ret; + ++ /* ++ * Now that the IOMMU TLB flush was done for the original GFN, drop ++ * the page references. The 2nd flush below is fine to make later, as ++ * whoever removes the page again from its new GFN will have to do ++ * another flush anyway. ++ */ ++ for ( i = 0; i < done; ++i ) ++ put_page(pages[i]); ++ + ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - done), done, + IOMMU_FLUSHF_added | IOMMU_FLUSHF_modified); + if ( unlikely(ret) && rc >= 0 ) +@@ -887,6 +903,8 @@ static int xenmem_add_to_physmap_batch(s + struct xen_add_to_physmap_batch *xatpb, + unsigned int extent) + { ++ union add_to_physmap_extra extra = {}; ++ + if ( xatpb->size < extent ) + return -EILSEQ; + +@@ -895,6 +913,19 @@ static int xenmem_add_to_physmap_batch(s + !guest_handle_subrange_okay(xatpb->errs, extent, xatpb->size - 1) ) + return -EFAULT; + ++ switch ( xatpb->space ) ++ { ++ case XENMAPSPACE_dev_mmio: ++ /* res0 is reserved for future use. */ ++ if ( xatpb->u.res0 ) ++ return -EOPNOTSUPP; ++ break; ++ ++ case XENMAPSPACE_gmfn_foreign: ++ extra.foreign_domid = xatpb->u.foreign_domid; ++ break; ++ } ++ + while ( xatpb->size > extent ) + { + xen_ulong_t idx; +@@ -907,8 +938,7 @@ static int xenmem_add_to_physmap_batch(s + extent, 1)) ) + return -EFAULT; + +- rc = xenmem_add_to_physmap_one(d, xatpb->space, +- xatpb->u, ++ rc = xenmem_add_to_physmap_one(d, xatpb->space, extra, + idx, _gfn(gpfn)); + + if ( unlikely(__copy_to_guest_offset(xatpb->errs, extent, &rc, 1)) ) +--- a/xen/include/xen/mm.h ++++ b/xen/include/xen/mm.h +@@ -583,8 +583,22 @@ void scrub_one_page(struct page_info *); + &(d)->xenpage_list : &(d)->page_list) + #endif + ++union add_to_physmap_extra { ++ /* ++ * XENMAPSPACE_gmfn: When deferring TLB flushes, a page reference needs ++ * to be kept until after the flush, so the page can't get removed from ++ * the domain (and re-used for another purpose) beforehand. By passing ++ * non-NULL, the caller of xenmem_add_to_physmap_one() indicates it wants ++ * to have ownership of such a reference transferred in the success case. ++ */ ++ struct page_info **ppage; ++ ++ /* XENMAPSPACE_gmfn_foreign */ ++ domid_t foreign_domid; ++}; ++ + int xenmem_add_to_physmap_one(struct domain *d, unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, gfn_t gfn); + + int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, diff --git a/xsa347-4.12-1.patch b/xsa347-4.12-1.patch new file mode 100644 index 0000000..3e1a4fd --- /dev/null +++ b/xsa347-4.12-1.patch @@ -0,0 +1,52 @@ +From: Jan Beulich +Subject: AMD/IOMMU: update live PTEs atomically + +Updating a live PTE word by word allows the IOMMU to see a partially +updated entry. Construct the new entry fully in a local variable and +then write the new entry by a single insn. + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -49,7 +49,7 @@ static unsigned int clear_iommu_pte_pres + IOMMU_PTE_PRESENT_SHIFT) ? + IOMMU_FLUSHF_modified : 0; + +- *pte = 0; ++ write_atomic(pte, 0); + unmap_domain_page(table); + + return flush_flags; +@@ -60,7 +60,7 @@ static unsigned int set_iommu_pde_presen + unsigned int next_level, bool iw, + bool ir) + { +- uint64_t maddr_next; ++ uint64_t maddr_next, full; + uint32_t addr_lo, addr_hi, entry; + bool old_present; + unsigned int flush_flags = IOMMU_FLUSHF_added; +@@ -119,7 +119,7 @@ static unsigned int set_iommu_pde_presen + if ( next_level == 0 ) + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_PTE_FC_MASK, IOMMU_PTE_FC_SHIFT, &entry); +- pde[1] = entry; ++ full = (uint64_t)entry << 32; + + /* mark next level as 'present' */ + set_field_in_reg_u32(addr_lo >> PAGE_SHIFT, 0, +@@ -131,7 +131,9 @@ static unsigned int set_iommu_pde_presen + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, entry, + IOMMU_PDE_PRESENT_MASK, + IOMMU_PDE_PRESENT_SHIFT, &entry); +- pde[0] = entry; ++ full |= entry; ++ ++ write_atomic((uint64_t *)pde, full); + + return flush_flags; + } diff --git a/xsa347-4.12-2.patch b/xsa347-4.12-2.patch new file mode 100644 index 0000000..25a987a --- /dev/null +++ b/xsa347-4.12-2.patch @@ -0,0 +1,80 @@ +From: Jan Beulich +Subject: AMD/IOMMU: ensure suitable ordering of DTE modifications + +DMA and interrupt translation should be enabled only after other +applicable DTE fields have been written. Similarly when disabling +translation or when moving a device between domains, translation should +first be disabled, before other entry fields get modified. Note however +that the "moving" aspect doesn't apply to the interrupt remapping side, +as domain specifics are maintained in the IRTEs here, not the DTE. We +also never disable interrupt remapping once it got enabled for a device +(the respective argument passed is always the immutable iommu_intremap). + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -162,7 +162,22 @@ void amd_iommu_set_root_page_table(uint3 + uint16_t domain_id, uint8_t paging_mode, + uint8_t valid) + { +- uint32_t addr_hi, addr_lo, entry; ++ uint32_t addr_hi, addr_lo, entry, dte0 = dte[0]; ++ ++ if ( valid || ++ get_field_from_reg_u32(dte0, IOMMU_DEV_TABLE_VALID_MASK, ++ IOMMU_DEV_TABLE_VALID_SHIFT) ) ++ { ++ set_field_in_reg_u32(IOMMU_CONTROL_DISABLED, dte0, ++ IOMMU_DEV_TABLE_TRANSLATION_VALID_MASK, ++ IOMMU_DEV_TABLE_TRANSLATION_VALID_SHIFT, &dte0); ++ set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, dte0, ++ IOMMU_DEV_TABLE_VALID_MASK, ++ IOMMU_DEV_TABLE_VALID_SHIFT, &dte0); ++ dte[0] = dte0; ++ smp_wmb(); ++ } ++ + set_field_in_reg_u32(domain_id, 0, + IOMMU_DEV_TABLE_DOMAIN_ID_MASK, + IOMMU_DEV_TABLE_DOMAIN_ID_SHIFT, &entry); +@@ -181,8 +196,9 @@ void amd_iommu_set_root_page_table(uint3 + IOMMU_DEV_TABLE_IO_READ_PERMISSION_MASK, + IOMMU_DEV_TABLE_IO_READ_PERMISSION_SHIFT, &entry); + dte[1] = entry; ++ smp_wmb(); + +- set_field_in_reg_u32(addr_lo >> PAGE_SHIFT, 0, ++ set_field_in_reg_u32(addr_lo >> PAGE_SHIFT, dte0, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_MASK, + IOMMU_DEV_TABLE_PAGE_TABLE_PTR_LOW_SHIFT, &entry); + set_field_in_reg_u32(paging_mode, entry, +@@ -195,7 +211,7 @@ void amd_iommu_set_root_page_table(uint3 + IOMMU_CONTROL_DISABLED, entry, + IOMMU_DEV_TABLE_VALID_MASK, + IOMMU_DEV_TABLE_VALID_SHIFT, &entry); +- dte[0] = entry; ++ write_atomic(&dte[0], entry); + } + + void iommu_dte_set_iotlb(uint32_t *dte, uint8_t i) +@@ -226,6 +242,7 @@ void __init amd_iommu_set_intremap_table + IOMMU_DEV_TABLE_INT_CONTROL_MASK, + IOMMU_DEV_TABLE_INT_CONTROL_SHIFT, &entry); + dte[5] = entry; ++ smp_wmb(); + + set_field_in_reg_u32(addr_lo >> 6, 0, + IOMMU_DEV_TABLE_INT_TABLE_PTR_LOW_MASK, +@@ -243,7 +260,7 @@ void __init amd_iommu_set_intremap_table + IOMMU_CONTROL_DISABLED, entry, + IOMMU_DEV_TABLE_INT_VALID_MASK, + IOMMU_DEV_TABLE_INT_VALID_SHIFT, &entry); +- dte[4] = entry; ++ write_atomic(&dte[4], entry); + } + + void __init iommu_dte_add_device_entry(uint32_t *dte, From 8cfa1f224d1fb6c99b74adb1ac15bbaef83f0eb8 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 29 Oct 2020 20:42:36 +0000 Subject: [PATCH 10/11] x86 PV guest INVLPG-like flushes may leave stale TLB entries [XSA-286, CVE-2020-27674] --- xen.spec | 29 ++- ...clearing-of-TF_kernel_mode-for-other.patch | 62 +++++++ ...and-L3-parts-of-the-walk-out-of-do_p.patch | 73 ++++++++ ...-mm-check-page-types-in-do_page_walk.patch | 170 +++++++++++++++++ ...ng-linear-page-tables-in-map_guest_l.patch | 92 ++++++++++ ...ng-linear-page-tables-in-guest_get_e.patch | 172 ++++++++++++++++++ ...ng-top-level-linear-page-tables-in-u.patch | 101 ++++++++++ ...use-of-linear-page-tables-to-shadow-.patch | 106 +++++++++++ 8 files changed, 801 insertions(+), 4 deletions(-) create mode 100644 xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch create mode 100644 xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch create mode 100644 xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch create mode 100644 xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch create mode 100644 xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch create mode 100644 xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch create mode 100644 xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch diff --git a/xen.spec b/xen.spec index 6192b88..3e150f2 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.3 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -152,6 +152,13 @@ Patch88: xsa346-4.12-1.patch Patch89: xsa346-4.12-2.patch Patch90: xsa347-4.12-1.patch Patch91: xsa347-4.12-2.patch +Patch92: xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch +Patch93: xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch +Patch94: xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch +Patch95: xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch +Patch96: xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch +Patch97: xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch +Patch98: xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch %if %build_qemutrad @@ -386,6 +393,13 @@ manage Xen virtual machines. %patch89 -p1 %patch90 -p1 %patch91 -p1 +%patch92 -p1 +%patch93 -p1 +%patch94 -p1 +%patch95 -p1 +%patch96 -p1 +%patch97 -p1 +%patch98 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -950,10 +964,17 @@ fi %endif %changelog +* Thu Oct 29 2020 Michael Young - 4.12.3-7 +- x86 PV guest INVLPG-like flushes may leave stale TLB entries + [XSA-286, CVE-2020-27674] (#1891092) + * Tue Oct 20 2020 Michael Young - 4.12.3-6 -- x86: Race condition in Xen mapping code [XSA-345] -- undue deferral of IOMMU TLB flushes [XSA-346] -- unsafe AMD IOMMU page table updates [XSA-347] +- x86: Race condition in Xen mapping code [XSA-345, CVE-2020-27672] + (#1891097) +- undue deferral of IOMMU TLB flushes [XSA-346, CVE-2020-27671] + (#1891093) +- unsafe AMD IOMMU page table updates [XSA-347, CVE-2020-27670] + (#1891088) * Tue Sep 22 2020 Michael Young - 4.12.3-5 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, diff --git a/xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch b/xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch new file mode 100644 index 0000000..e7f9a6a --- /dev/null +++ b/xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch @@ -0,0 +1,62 @@ +From: Jan Beulich +Subject: x86: don't allow clearing of TF_kernel_mode for other than 64-bit PV + +The flag is really only meant for those, both HVM and 32-bit PV tell +kernel from user mode based on CPL/RPL. Remove the all-question-marks +comment and let's be on the safe side here and also suppress clearing +for 32-bit PV (this isn't a fast path after all). + +Remove no longer necessary is_pv_32bit_*() from sh_update_cr3() and +sh_walk_guest_tables(). Note that shadow_one_bit_disable() already +assumes the new behavior. + +Signed-off-by: Jan Beulich +Reviewed-by: Wei Liu +Acked-by: George Dunlap +Acked-by: Andrew Cooper + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 019dc57459..607b346737 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -840,9 +840,15 @@ int arch_set_info_guest( + return -EINVAL; + } + +- v->arch.flags &= ~TF_kernel_mode; +- if ( (flags & VGCF_in_kernel) || is_hvm_domain(d)/*???*/ ) +- v->arch.flags |= TF_kernel_mode; ++ v->arch.flags |= TF_kernel_mode; ++ if ( unlikely(!(flags & VGCF_in_kernel)) && ++ /* ++ * TF_kernel_mode is only allowed to be clear for 64-bit PV. See ++ * update_cr3(), sh_update_cr3(), sh_walk_guest_tables(), and ++ * shadow_one_bit_disable() for why that is. ++ */ ++ !is_hvm_domain(d) && !is_pv_32bit_domain(d) ) ++ v->arch.flags &= ~TF_kernel_mode; + + v->arch.vgc_flags = flags; + +diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c +index 3e5651d029..4ae8e05ec8 100644 +--- a/xen/arch/x86/mm/shadow/multi.c ++++ b/xen/arch/x86/mm/shadow/multi.c +@@ -180,7 +180,7 @@ sh_walk_guest_tables(struct vcpu *v, unsigned long va, walk_t *gw, + INVALID_MFN, v->arch.paging.shadow.gl3e); + #else /* 32 or 64 */ + const struct domain *d = v->domain; +- mfn_t root_mfn = ((v->arch.flags & TF_kernel_mode) || is_pv_32bit_domain(d) ++ mfn_t root_mfn = (v->arch.flags & TF_kernel_mode + ? pagetable_get_mfn(v->arch.guest_table) + : pagetable_get_mfn(v->arch.guest_table_user)); + void *root_map = map_domain_page(root_mfn); +@@ -4025,7 +4025,7 @@ sh_update_cr3(struct vcpu *v, int do_locking, bool noflush) + v, (unsigned long)pagetable_get_pfn(v->arch.guest_table)); + + #if GUEST_PAGING_LEVELS == 4 +- if ( !(v->arch.flags & TF_kernel_mode) && !is_pv_32bit_domain(d) ) ++ if ( !(v->arch.flags & TF_kernel_mode) ) + gmfn = pagetable_get_mfn(v->arch.guest_table_user); + else + #endif diff --git a/xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch b/xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch new file mode 100644 index 0000000..d372955 --- /dev/null +++ b/xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch @@ -0,0 +1,73 @@ +From: Jan Beulich +Subject: x86/mm: split L4 and L3 parts of the walk out of do_page_walk() + +The L3 one at least is going to be re-used by a subsequent patch, and +splitting the L4 one then as well seems only natural. + +This is part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 7246ee50ef..9c89c7d076 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -44,26 +44,47 @@ unsigned int __read_mostly m2p_compat_vstart = __HYPERVISOR_COMPAT_VIRT_START; + + l2_pgentry_t *compat_idle_pg_table_l2; + +-void *do_page_walk(struct vcpu *v, unsigned long addr) ++static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + { +- unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); +- l4_pgentry_t l4e, *l4t; +- l3_pgentry_t l3e, *l3t; +- l2_pgentry_t l2e, *l2t; +- l1_pgentry_t l1e, *l1t; ++ unsigned long mfn = pagetable_get_pfn(root); ++ l4_pgentry_t *l4t, l4e; + +- if ( !is_pv_vcpu(v) || !is_canonical_address(addr) ) +- return NULL; ++ if ( !is_canonical_address(addr) ) ++ return l4e_empty(); + + l4t = map_domain_page(_mfn(mfn)); + l4e = l4t[l4_table_offset(addr)]; + unmap_domain_page(l4t); ++ ++ return l4e; ++} ++ ++static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) ++{ ++ l4_pgentry_t l4e = page_walk_get_l4e(root, addr); ++ l3_pgentry_t *l3t, l3e; ++ + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) +- return NULL; ++ return l3e_empty(); + + l3t = map_l3t_from_l4e(l4e); + l3e = l3t[l3_table_offset(addr)]; + unmap_domain_page(l3t); ++ ++ return l3e; ++} ++ ++void *do_page_walk(struct vcpu *v, unsigned long addr) ++{ ++ l3_pgentry_t l3e; ++ l2_pgentry_t l2e, *l2t; ++ l1_pgentry_t l1e, *l1t; ++ unsigned long mfn; ++ ++ if ( !is_pv_vcpu(v) ) ++ return NULL; ++ ++ l3e = page_walk_get_l3e(v->arch.guest_table, addr); + mfn = l3e_get_pfn(l3e); + if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) + return NULL; diff --git a/xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch b/xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch new file mode 100644 index 0000000..ce014e7 --- /dev/null +++ b/xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch @@ -0,0 +1,170 @@ +From: Jan Beulich +Subject: x86/mm: check page types in do_page_walk() + +For page table entries read to be guaranteed valid, transiently locking +the pages and validating their types is necessary. Note that guest use +of linear page tables is intentionally not taken into account here, as +ordinary data (guest stacks) can't possibly live inside page tables. + +This is part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 9c89c7d076..c764237651 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -46,15 +46,29 @@ l2_pgentry_t *compat_idle_pg_table_l2; + + static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + { +- unsigned long mfn = pagetable_get_pfn(root); +- l4_pgentry_t *l4t, l4e; ++ mfn_t mfn = pagetable_get_mfn(root); ++ /* current's root page table can't disappear under our feet. */ ++ bool need_lock = !mfn_eq(mfn, pagetable_get_mfn(current->arch.guest_table)); ++ struct page_info *pg; ++ l4_pgentry_t l4e = l4e_empty(); + + if ( !is_canonical_address(addr) ) + return l4e_empty(); + +- l4t = map_domain_page(_mfn(mfn)); +- l4e = l4t[l4_table_offset(addr)]; +- unmap_domain_page(l4t); ++ pg = mfn_to_page(mfn); ++ if ( need_lock && !page_lock(pg) ) ++ return l4e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l4_page_table ) ++ { ++ l4_pgentry_t *l4t = map_domain_page(mfn); ++ ++ l4e = l4t[l4_table_offset(addr)]; ++ unmap_domain_page(l4t); ++ } ++ ++ if ( need_lock ) ++ page_unlock(pg); + + return l4e; + } +@@ -62,14 +76,26 @@ static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + { + l4_pgentry_t l4e = page_walk_get_l4e(root, addr); +- l3_pgentry_t *l3t, l3e; ++ mfn_t mfn = l4e_get_mfn(l4e); ++ struct page_info *pg; ++ l3_pgentry_t l3e = l3e_empty(); + + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) + return l3e_empty(); + +- l3t = map_l3t_from_l4e(l4e); +- l3e = l3t[l3_table_offset(addr)]; +- unmap_domain_page(l3t); ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l3e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l3_page_table ) ++ { ++ l3_pgentry_t *l3t = map_domain_page(mfn); ++ ++ l3e = l3t[l3_table_offset(addr)]; ++ unmap_domain_page(l3t); ++ } ++ ++ page_unlock(pg); + + return l3e; + } +@@ -77,44 +103,67 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +- l2_pgentry_t l2e, *l2t; +- l1_pgentry_t l1e, *l1t; +- unsigned long mfn; ++ l2_pgentry_t l2e = l2e_empty(); ++ l1_pgentry_t l1e = l1e_empty(); ++ mfn_t mfn; ++ struct page_info *pg; + + if ( !is_pv_vcpu(v) ) + return NULL; + + l3e = page_walk_get_l3e(v->arch.guest_table, addr); +- mfn = l3e_get_pfn(l3e); +- if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ mfn = l3e_get_mfn(l3e); ++ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + if ( (l3e_get_flags(l3e) & _PAGE_PSE) ) + { +- mfn += PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1)); ++ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1))); + goto ret; + } + +- l2t = map_domain_page(_mfn(mfn)); +- l2e = l2t[l2_table_offset(addr)]; +- unmap_domain_page(l2t); +- mfn = l2e_get_pfn(l2e); +- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return NULL; ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) ++ { ++ const l2_pgentry_t *l2t = map_domain_page(mfn); ++ ++ l2e = l2t[l2_table_offset(addr)]; ++ unmap_domain_page(l2t); ++ } ++ ++ page_unlock(pg); ++ ++ mfn = l2e_get_mfn(l2e); ++ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + if ( (l2e_get_flags(l2e) & _PAGE_PSE) ) + { +- mfn += PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1)); ++ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1))); + goto ret; + } + +- l1t = map_domain_page(_mfn(mfn)); +- l1e = l1t[l1_table_offset(addr)]; +- unmap_domain_page(l1t); +- mfn = l1e_get_pfn(l1e); +- if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return NULL; ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) ++ { ++ const l1_pgentry_t *l1t = map_domain_page(mfn); ++ ++ l1e = l1t[l1_table_offset(addr)]; ++ unmap_domain_page(l1t); ++ } ++ ++ page_unlock(pg); ++ ++ mfn = l1e_get_mfn(l1e); ++ if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + + ret: +- return map_domain_page(_mfn(mfn)) + (addr & ~PAGE_MASK); ++ return map_domain_page(mfn) + (addr & ~PAGE_MASK); + } + + /* diff --git a/xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch b/xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch new file mode 100644 index 0000000..4e885c7 --- /dev/null +++ b/xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch @@ -0,0 +1,92 @@ +From: Jan Beulich +Subject: x86/mm: avoid using linear page tables in map_guest_l1e() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace the linear L2 table access by an actual page walk. + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Signed-off-by: Roger Pau MonnĂ© +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c +index 2b0dadc8da..acebf9e957 100644 +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -40,11 +40,14 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) + if ( unlikely(!__addr_ok(linear)) ) + return NULL; + +- /* Find this l1e and its enclosing l1mfn in the linear map. */ +- if ( __copy_from_user(&l2e, +- &__linear_l2_table[l2_linear_offset(linear)], +- sizeof(l2_pgentry_t)) ) ++ if ( unlikely(!(current->arch.flags & TF_kernel_mode)) ) ++ { ++ ASSERT_UNREACHABLE(); + return NULL; ++ } ++ ++ /* Find this l1e and its enclosing l1mfn. */ ++ l2e = page_walk_get_l2e(current->arch.guest_table, linear); + + /* Check flags that it will be safe to read the l1e. */ + if ( (l2e_get_flags(l2e) & (_PAGE_PRESENT | _PAGE_PSE)) != _PAGE_PRESENT ) +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index c764237651..062069a908 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -100,6 +100,34 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + return l3e; + } + ++l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) ++{ ++ l3_pgentry_t l3e = page_walk_get_l3e(root, addr); ++ mfn_t mfn = l3e_get_mfn(l3e); ++ struct page_info *pg; ++ l2_pgentry_t l2e = l2e_empty(); ++ ++ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || ++ (l3e_get_flags(l3e) & _PAGE_PSE) ) ++ return l2e_empty(); ++ ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l2e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) ++ { ++ l2_pgentry_t *l2t = map_domain_page(mfn); ++ ++ l2e = l2t[l2_table_offset(addr)]; ++ unmap_domain_page(l2t); ++ } ++ ++ page_unlock(pg); ++ ++ return l2e; ++} ++ + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 845556eb6d..504f3fdc14 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -585,7 +585,9 @@ void audit_domains(void); + void make_cr3(struct vcpu *v, mfn_t mfn); + void update_cr3(struct vcpu *v); + int vcpu_destroy_pagetables(struct vcpu *); ++ + void *do_page_walk(struct vcpu *v, unsigned long addr); ++l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); + + int __sync_local_execstate(void); + diff --git a/xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch b/xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch new file mode 100644 index 0000000..b53c6ac --- /dev/null +++ b/xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch @@ -0,0 +1,172 @@ +From: Jan Beulich +Subject: x86/mm: avoid using linear page tables in guest_get_eff_kern_l1e() + +First of all drop guest_get_eff_l1e() entirely - there's no actual user +of it: pv_ro_page_fault() has a guest_kernel_mode() conditional around +its only call site. + +Then replace the linear L1 table access by an actual page walk. + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c +index acebf9e957..7624447246 100644 +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -59,27 +59,6 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) + } + + /* +- * Read the guest's l1e that maps this address, from the kernel-mode +- * page tables. +- */ +-static l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) +-{ +- struct vcpu *curr = current; +- const bool user_mode = !(curr->arch.flags & TF_kernel_mode); +- l1_pgentry_t l1e; +- +- if ( user_mode ) +- toggle_guest_pt(curr); +- +- l1e = guest_get_eff_l1e(linear); +- +- if ( user_mode ) +- toggle_guest_pt(curr); +- +- return l1e; +-} +- +-/* + * Map a guest's LDT page (covering the byte at @offset from start of the LDT) + * into Xen's virtual range. Returns true if the mapping changed, false + * otherwise. +diff --git a/xen/arch/x86/pv/mm.h b/xen/arch/x86/pv/mm.h +index 976209ba4c..cc4ee1affb 100644 +--- a/xen/arch/x86/pv/mm.h ++++ b/xen/arch/x86/pv/mm.h +@@ -5,19 +5,19 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn); + + int new_guest_cr3(mfn_t mfn); + +-/* Read a PV guest's l1e that maps this linear address. */ +-static inline l1_pgentry_t guest_get_eff_l1e(unsigned long linear) ++/* ++ * Read the guest's l1e that maps this address, from the kernel-mode ++ * page tables. ++ */ ++static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) + { +- l1_pgentry_t l1e; ++ l1_pgentry_t l1e = l1e_empty(); + + ASSERT(!paging_mode_translate(current->domain)); + ASSERT(!paging_mode_external(current->domain)); + +- if ( unlikely(!__addr_ok(linear)) || +- __copy_from_user(&l1e, +- &__linear_l1_table[l1_linear_offset(linear)], +- sizeof(l1_pgentry_t)) ) +- l1e = l1e_empty(); ++ if ( likely(__addr_ok(linear)) ) ++ l1e = page_walk_get_l1e(current->arch.guest_table, linear); + + return l1e; + } +diff --git a/xen/arch/x86/pv/ro-page-fault.c b/xen/arch/x86/pv/ro-page-fault.c +index e7a7179dda..ebac3f476a 100644 +--- a/xen/arch/x86/pv/ro-page-fault.c ++++ b/xen/arch/x86/pv/ro-page-fault.c +@@ -360,7 +360,7 @@ int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs) + bool mmio_ro; + + /* Attempt to read the PTE that maps the VA being accessed. */ +- pte = guest_get_eff_l1e(addr); ++ pte = guest_get_eff_kern_l1e(addr); + + /* We are only looking for read-only mappings */ + if ( ((l1e_get_flags(pte) & (_PAGE_PRESENT | _PAGE_RW)) != _PAGE_PRESENT) ) +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 062069a908..7dbfabf267 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -128,6 +128,62 @@ l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) + return l2e; + } + ++/* ++ * For now no "set_accessed" parameter, as all callers want it set to true. ++ * For now also no "set_dirty" parameter, as all callers deal with r/o ++ * mappings, and we don't want to set the dirty bit there (conflicts with ++ * CET-SS). However, as there are CPUs which may set the dirty bit on r/o ++ * PTEs, the logic below tolerates the bit becoming set "behind our backs". ++ */ ++l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr) ++{ ++ l2_pgentry_t l2e = page_walk_get_l2e(root, addr); ++ mfn_t mfn = l2e_get_mfn(l2e); ++ struct page_info *pg; ++ l1_pgentry_t l1e = l1e_empty(); ++ ++ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || ++ (l2e_get_flags(l2e) & _PAGE_PSE) ) ++ return l1e_empty(); ++ ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l1e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) ++ { ++ l1_pgentry_t *l1t = map_domain_page(mfn); ++ ++ l1e = l1t[l1_table_offset(addr)]; ++ ++ if ( (l1e_get_flags(l1e) & (_PAGE_ACCESSED | _PAGE_PRESENT)) == ++ _PAGE_PRESENT ) ++ { ++ l1_pgentry_t ol1e = l1e; ++ ++ l1e_add_flags(l1e, _PAGE_ACCESSED); ++ /* ++ * Best effort only; with the lock held the page shouldn't ++ * change anyway, except for the dirty bit to perhaps become set. ++ */ ++ while ( cmpxchg(&l1e_get_intpte(l1t[l1_table_offset(addr)]), ++ l1e_get_intpte(ol1e), l1e_get_intpte(l1e)) != ++ l1e_get_intpte(ol1e) && ++ !(l1e_get_flags(l1e) & _PAGE_DIRTY) ) ++ { ++ l1e_add_flags(ol1e, _PAGE_DIRTY); ++ l1e_add_flags(l1e, _PAGE_DIRTY); ++ } ++ } ++ ++ unmap_domain_page(l1t); ++ } ++ ++ page_unlock(pg); ++ ++ return l1e; ++} ++ + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 504f3fdc14..0aed4c3b77 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -588,6 +588,7 @@ int vcpu_destroy_pagetables(struct vcpu *); + + void *do_page_walk(struct vcpu *v, unsigned long addr); + l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); ++l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr); + + int __sync_local_execstate(void); + diff --git a/xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch b/xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch new file mode 100644 index 0000000..29deb59 --- /dev/null +++ b/xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch @@ -0,0 +1,101 @@ +From: Jan Beulich +Subject: x86/mm: avoid using top level linear page tables in + {,un}map_domain_page() + +Move the page table recursion two levels down. This entails avoiding +to free the recursive mapping prematurely in free_perdomain_mappings(). + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c +index 4a07cfb18e..660bd06aaf 100644 +--- a/xen/arch/x86/domain_page.c ++++ b/xen/arch/x86/domain_page.c +@@ -65,7 +65,8 @@ void __init mapcache_override_current(struct vcpu *v) + #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) + #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) + #define MAPCACHE_L1ENT(idx) \ +- __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] ++ ((l1_pgentry_t *)(MAPCACHE_VIRT_START | \ ++ ((L2_PAGETABLE_ENTRIES - 1) << L2_PAGETABLE_SHIFT)))[idx] + + void *map_domain_page(mfn_t mfn) + { +@@ -235,6 +236,7 @@ int mapcache_domain_init(struct domain *d) + { + struct mapcache_domain *dcache = &d->arch.pv.mapcache; + unsigned int bitmap_pages; ++ int rc; + + ASSERT(is_pv_domain(d)); + +@@ -243,8 +245,10 @@ int mapcache_domain_init(struct domain *d) + return 0; + #endif + ++ BUILD_BUG_ON(MAPCACHE_VIRT_START & ((1 << L3_PAGETABLE_SHIFT) - 1)); + BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + +- 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > ++ 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) + ++ (1U << L2_PAGETABLE_SHIFT) > + MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); + bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); + dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; +@@ -253,9 +257,25 @@ int mapcache_domain_init(struct domain *d) + + spin_lock_init(&dcache->lock); + +- return create_perdomain_mapping(d, (unsigned long)dcache->inuse, +- 2 * bitmap_pages + 1, +- NIL(l1_pgentry_t *), NULL); ++ rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, ++ 2 * bitmap_pages + 1, ++ NIL(l1_pgentry_t *), NULL); ++ if ( !rc ) ++ { ++ /* ++ * Install mapping of our L2 table into its own last slot, for easy ++ * access to the L1 entries via MAPCACHE_L1ENT(). ++ */ ++ l3_pgentry_t *l3t = __map_domain_page(d->arch.perdomain_l3_pg); ++ l3_pgentry_t l3e = l3t[l3_table_offset(MAPCACHE_VIRT_END)]; ++ l2_pgentry_t *l2t = map_l2t_from_l3e(l3e); ++ ++ l2e_get_intpte(l2t[L2_PAGETABLE_ENTRIES - 1]) = l3e_get_intpte(l3e); ++ unmap_domain_page(l2t); ++ unmap_domain_page(l3t); ++ } ++ ++ return rc; + } + + int mapcache_vcpu_init(struct vcpu *v) +@@ -346,7 +366,7 @@ mfn_t domain_page_map_to_mfn(const void *ptr) + else + { + ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); +- pl1e = &__linear_l1_table[l1_linear_offset(va)]; ++ pl1e = &MAPCACHE_L1ENT(PFN_DOWN(va - MAPCACHE_VIRT_START)); + } + + return l1e_get_mfn(*pl1e); +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index b4c90bd054..d234e191a6 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -6071,6 +6071,10 @@ void free_perdomain_mappings(struct domain *d) + { + struct page_info *l1pg = l2e_get_page(l2tab[j]); + ++ /* mapcache_domain_init() installs a recursive entry. */ ++ if ( l1pg == l2pg ) ++ continue; ++ + if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) + { + l1_pgentry_t *l1tab = __map_domain_page(l1pg); diff --git a/xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch b/xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch new file mode 100644 index 0000000..bed7b6c --- /dev/null +++ b/xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch @@ -0,0 +1,106 @@ +From: Jan Beulich +Subject: x86/mm: restrict use of linear page tables to shadow mode code + +Other code does not require them to be set up anymore, so restrict when +to populate the respective L4 slot and reduce visibility of the +accessors. + +While with the removal of all uses the vulnerability is actually fixed, +removing the creation of the linear mapping adds an extra layer of +protection. Similarly reducing visibility of the accessors mostly +eliminates the risk of undue re-introduction of uses of the linear +mappings. + +This is (not strictly) part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index d234e191a6..51826ecb14 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1786,9 +1786,10 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, + l4t[l4_table_offset(PCI_MCFG_VIRT_START)] = + idle_pg_table[l4_table_offset(PCI_MCFG_VIRT_START)]; + +- /* Slot 258: Self linear mappings. */ ++ /* Slot 258: Self linear mappings (shadow pt only). */ + ASSERT(!mfn_eq(l4mfn, INVALID_MFN)); + l4t[l4_table_offset(LINEAR_PT_VIRT_START)] = ++ !shadow_mode_external(d) ? l4e_empty() : + l4e_from_mfn(l4mfn, __PAGE_HYPERVISOR_RW); + + /* Slot 259: Shadow linear mappings (if applicable) .*/ +diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h +index 580ef3e29e..f082f02670 100644 +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -135,6 +135,15 @@ enum { + # define GUEST_PTE_SIZE 4 + #endif + ++/* Where to find each level of the linear mapping */ ++#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) ++#define __linear_l2_table \ ++ ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) ++#define __linear_l3_table \ ++ ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) ++#define __linear_l4_table \ ++ ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) ++ + /****************************************************************************** + * Auditing routines + */ +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 7dbfabf267..577938bc17 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -833,9 +833,6 @@ void __init paging_init(void) + + machine_to_phys_mapping_valid = 1; + +- /* Set up linear page table mapping. */ +- l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], +- l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR_RW)); + return; + + nomem: +diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h +index feeb456bee..01a56dcab0 100644 +--- a/xen/include/asm-x86/config.h ++++ b/xen/include/asm-x86/config.h +@@ -193,7 +193,7 @@ extern unsigned char boot_edid_info[128]; + */ + #define PCI_MCFG_VIRT_START (PML4_ADDR(257)) + #define PCI_MCFG_VIRT_END (PCI_MCFG_VIRT_START + PML4_ENTRY_BYTES) +-/* Slot 258: linear page table (guest table). */ ++/* Slot 258: linear page table (monitor table, HVM only). */ + #define LINEAR_PT_VIRT_START (PML4_ADDR(258)) + #define LINEAR_PT_VIRT_END (LINEAR_PT_VIRT_START + PML4_ENTRY_BYTES) + /* Slot 259: linear page table (shadow table). */ +diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h +index c1e92937c0..e72c277b9f 100644 +--- a/xen/include/asm-x86/page.h ++++ b/xen/include/asm-x86/page.h +@@ -274,19 +274,6 @@ void copy_page_sse2(void *, const void *); + #define vmap_to_mfn(va) _mfn(l1e_get_pfn(*virt_to_xen_l1e((unsigned long)(va)))) + #define vmap_to_page(va) mfn_to_page(vmap_to_mfn(va)) + +-#endif /* !defined(__ASSEMBLY__) */ +- +-/* Where to find each level of the linear mapping */ +-#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) +-#define __linear_l2_table \ +- ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) +-#define __linear_l3_table \ +- ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) +-#define __linear_l4_table \ +- ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) +- +- +-#ifndef __ASSEMBLY__ + extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES]; + extern l2_pgentry_t *compat_idle_pg_table_l2; + extern unsigned int m2p_compat_vstart; From 5d5bea92a55548d3da84ac0b883bab6dae1c86d6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 3 Nov 2020 22:14:30 +0000 Subject: [PATCH 11/11] revised patch for XSA-286 (mitigating performance impact) --- xen.spec | 19 +- ...clearing-of-TF_kernel_mode-for-other.patch | 62 ------- ...H_TLB_GLOBAL-in-do_mmu_update-for-XP.patch | 58 ++++++ ...and-L3-parts-of-the-walk-out-of-do_p.patch | 73 -------- ...-in-response-to-paging-structure-cha.patch | 166 +++++++++++++++++ ...-mm-check-page-types-in-do_page_walk.patch | 170 ----------------- ...ng-linear-page-tables-in-map_guest_l.patch | 92 ---------- ...ng-linear-page-tables-in-guest_get_e.patch | 172 ------------------ ...ng-top-level-linear-page-tables-in-u.patch | 101 ---------- ...use-of-linear-page-tables-to-shadow-.patch | 106 ----------- 10 files changed, 230 insertions(+), 789 deletions(-) delete mode 100644 xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch create mode 100644 xsa286-4.12-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch delete mode 100644 xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch create mode 100644 xsa286-4.12-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch delete mode 100644 xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch delete mode 100644 xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch delete mode 100644 xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch delete mode 100644 xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch delete mode 100644 xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch diff --git a/xen.spec b/xen.spec index 3e150f2..ec7ce43 100644 --- a/xen.spec +++ b/xen.spec @@ -59,7 +59,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.3 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -152,13 +152,8 @@ Patch88: xsa346-4.12-1.patch Patch89: xsa346-4.12-2.patch Patch90: xsa347-4.12-1.patch Patch91: xsa347-4.12-2.patch -Patch92: xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch -Patch93: xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch -Patch94: xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch -Patch95: xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch -Patch96: xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch -Patch97: xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch -Patch98: xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch +Patch92: xsa286-4.12-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch +Patch93: xsa286-4.12-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch %if %build_qemutrad @@ -395,11 +390,6 @@ manage Xen virtual machines. %patch91 -p1 %patch92 -p1 %patch93 -p1 -%patch94 -p1 -%patch95 -p1 -%patch96 -p1 -%patch97 -p1 -%patch98 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -964,6 +954,9 @@ fi %endif %changelog +* Tue Nov 03 2020 Michael Young - 4.12.3-8 +- revised patch for XSA-286 (mitigating performance impact) + * Thu Oct 29 2020 Michael Young - 4.12.3-7 - x86 PV guest INVLPG-like flushes may leave stale TLB entries [XSA-286, CVE-2020-27674] (#1891092) diff --git a/xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch b/xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch deleted file mode 100644 index e7f9a6a..0000000 --- a/xsa286-4.12-0001-x86-don-t-allow-clearing-of-TF_kernel_mode-for-other.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: Jan Beulich -Subject: x86: don't allow clearing of TF_kernel_mode for other than 64-bit PV - -The flag is really only meant for those, both HVM and 32-bit PV tell -kernel from user mode based on CPL/RPL. Remove the all-question-marks -comment and let's be on the safe side here and also suppress clearing -for 32-bit PV (this isn't a fast path after all). - -Remove no longer necessary is_pv_32bit_*() from sh_update_cr3() and -sh_walk_guest_tables(). Note that shadow_one_bit_disable() already -assumes the new behavior. - -Signed-off-by: Jan Beulich -Reviewed-by: Wei Liu -Acked-by: George Dunlap -Acked-by: Andrew Cooper - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 019dc57459..607b346737 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -840,9 +840,15 @@ int arch_set_info_guest( - return -EINVAL; - } - -- v->arch.flags &= ~TF_kernel_mode; -- if ( (flags & VGCF_in_kernel) || is_hvm_domain(d)/*???*/ ) -- v->arch.flags |= TF_kernel_mode; -+ v->arch.flags |= TF_kernel_mode; -+ if ( unlikely(!(flags & VGCF_in_kernel)) && -+ /* -+ * TF_kernel_mode is only allowed to be clear for 64-bit PV. See -+ * update_cr3(), sh_update_cr3(), sh_walk_guest_tables(), and -+ * shadow_one_bit_disable() for why that is. -+ */ -+ !is_hvm_domain(d) && !is_pv_32bit_domain(d) ) -+ v->arch.flags &= ~TF_kernel_mode; - - v->arch.vgc_flags = flags; - -diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c -index 3e5651d029..4ae8e05ec8 100644 ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -180,7 +180,7 @@ sh_walk_guest_tables(struct vcpu *v, unsigned long va, walk_t *gw, - INVALID_MFN, v->arch.paging.shadow.gl3e); - #else /* 32 or 64 */ - const struct domain *d = v->domain; -- mfn_t root_mfn = ((v->arch.flags & TF_kernel_mode) || is_pv_32bit_domain(d) -+ mfn_t root_mfn = (v->arch.flags & TF_kernel_mode - ? pagetable_get_mfn(v->arch.guest_table) - : pagetable_get_mfn(v->arch.guest_table_user)); - void *root_map = map_domain_page(root_mfn); -@@ -4025,7 +4025,7 @@ sh_update_cr3(struct vcpu *v, int do_locking, bool noflush) - v, (unsigned long)pagetable_get_pfn(v->arch.guest_table)); - - #if GUEST_PAGING_LEVELS == 4 -- if ( !(v->arch.flags & TF_kernel_mode) && !is_pv_32bit_domain(d) ) -+ if ( !(v->arch.flags & TF_kernel_mode) ) - gmfn = pagetable_get_mfn(v->arch.guest_table_user); - else - #endif diff --git a/xsa286-4.12-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch b/xsa286-4.12-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch new file mode 100644 index 0000000..4ea103e --- /dev/null +++ b/xsa286-4.12-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch @@ -0,0 +1,58 @@ +From b1d6f37aa5aa9f3fc5a269b9dd21b7feb7444be0 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Thu, 22 Oct 2020 11:28:58 +0100 +Subject: [PATCH 1/2] x86/pv: Drop FLUSH_TLB_GLOBAL in do_mmu_update() for XPTI + +c/s 9d1d31ad9498 "x86: slightly reduce Meltdown band-aid overhead" removed the +use of Global TLB flushes on the Xen entry path, but added a FLUSH_TLB_GLOBAL +to the L4 path in do_mmu_update(). + +However, this was unnecessary. + +It is the guests responsibility to perform appropriate TLB flushing if the L4 +modification altered an established mapping in a flush-relevant way. In this +case, an MMUEXT_OP hypercall will follow. The case which Xen needs to cover +is when new mappings are created, and the resync on the exit-to-guest path +covers this correctly. + +There is a corner case with multiple vCPUs in hypercalls at the same time, +which 9d1d31ad9498 changed, and this patch changes back to its original XPTI +behaviour. + +Architecturally, established TLB entries can continue to be used until the +broadcast flush has completed. Therefore, even with concurrent hypercalls, +the guest cannot depend on older mappings not being used until an MMUEXT_OP +hypercall completes. Xen's implementation of guest-initiated flushes will +take correct effect on top of an in-progress hypercall, picking up new mapping +setting before the other vCPU's MMUEXT_OP completes. + +Note: The correctness of this change is not impacted by whether XPTI uses +global mappings or not. Correctness there depends on the behaviour of Xen on +the entry/exit paths when switching two/from the XPTI "shadow" pagetables. + +This is (not really) XSA-286 (but necessary to simplify the logic). + +Fixes: 9d1d31ad9498 ("x86: slightly reduce Meltdown band-aid overhead") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 055e1c3a3d95b1e753148369fbc4ba48782dd602) +--- + xen/arch/x86/mm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index e7b8f4ee4b..86f31b334f 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4301,7 +4301,7 @@ long do_mmu_update( + + cpumask_andnot(mask, pt_owner->dirty_cpumask, cpumask_of(cpu)); + if ( !cpumask_empty(mask) ) +- flush_mask(mask, FLUSH_TLB_GLOBAL | FLUSH_ROOT_PGTBL); ++ flush_mask(mask, FLUSH_ROOT_PGTBL); + } + + perfc_add(num_page_updates, i); +-- +2.20.1 + diff --git a/xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch b/xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch deleted file mode 100644 index d372955..0000000 --- a/xsa286-4.12-0002-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch +++ /dev/null @@ -1,73 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: split L4 and L3 parts of the walk out of do_page_walk() - -The L3 one at least is going to be re-used by a subsequent patch, and -splitting the L4 one then as well seems only natural. - -This is part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 7246ee50ef..9c89c7d076 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -44,26 +44,47 @@ unsigned int __read_mostly m2p_compat_vstart = __HYPERVISOR_COMPAT_VIRT_START; - - l2_pgentry_t *compat_idle_pg_table_l2; - --void *do_page_walk(struct vcpu *v, unsigned long addr) -+static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - { -- unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); -- l4_pgentry_t l4e, *l4t; -- l3_pgentry_t l3e, *l3t; -- l2_pgentry_t l2e, *l2t; -- l1_pgentry_t l1e, *l1t; -+ unsigned long mfn = pagetable_get_pfn(root); -+ l4_pgentry_t *l4t, l4e; - -- if ( !is_pv_vcpu(v) || !is_canonical_address(addr) ) -- return NULL; -+ if ( !is_canonical_address(addr) ) -+ return l4e_empty(); - - l4t = map_domain_page(_mfn(mfn)); - l4e = l4t[l4_table_offset(addr)]; - unmap_domain_page(l4t); -+ -+ return l4e; -+} -+ -+static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) -+{ -+ l4_pgentry_t l4e = page_walk_get_l4e(root, addr); -+ l3_pgentry_t *l3t, l3e; -+ - if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) -- return NULL; -+ return l3e_empty(); - - l3t = map_l3t_from_l4e(l4e); - l3e = l3t[l3_table_offset(addr)]; - unmap_domain_page(l3t); -+ -+ return l3e; -+} -+ -+void *do_page_walk(struct vcpu *v, unsigned long addr) -+{ -+ l3_pgentry_t l3e; -+ l2_pgentry_t l2e, *l2t; -+ l1_pgentry_t l1e, *l1t; -+ unsigned long mfn; -+ -+ if ( !is_pv_vcpu(v) ) -+ return NULL; -+ -+ l3e = page_walk_get_l3e(v->arch.guest_table, addr); - mfn = l3e_get_pfn(l3e); - if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) - return NULL; diff --git a/xsa286-4.12-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch b/xsa286-4.12-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch new file mode 100644 index 0000000..32ed234 --- /dev/null +++ b/xsa286-4.12-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch @@ -0,0 +1,166 @@ +From 4100d463dbdd95d85fabe387dd5676bed75f65f7 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Mon, 19 Oct 2020 15:51:22 +0100 +Subject: [PATCH 2/2] x86/pv: Flush TLB in response to paging structure changes + +With MMU_UPDATE, a PV guest can make changes to higher level pagetables. This +is safe from Xen's point of view (as the update only affects guest mappings), +and the guest is required to flush (if necessary) after making updates. + +However, Xen's use of linear pagetables (UPDATE_VA_MAPPING, GNTTABOP_map, +writeable pagetables, etc.) is an implementation detail outside of the +API/ABI. + +Changes in the paging structure require invalidations in the linear pagetable +range for subsequent accesses into the linear pagetables to access non-stale +mappings. Xen must provide suitable flushing to prevent intermixed guest +actions from accidentally accessing/modifying the wrong pagetable. + +For all L2 and higher modifications, flush the TLB. PV guests cannot create +L2 or higher entries with the Global bit set, so no mappings established in +the linear range can be global. (This could in principle be an order 39 flush +starting at LINEAR_PT_VIRT_START, but no such mechanism exists in practice.) + +Express the necessary flushes as a set of booleans which accumulate across the +operation. Comment the flushing logic extensively. + +This is XSA-286. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 16a20963b3209788f2c0d3a3eebb7d92f03f5883) +--- + xen/arch/x86/mm.c | 69 ++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 59 insertions(+), 10 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 86f31b334f..db4cfdf20b 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4005,7 +4005,8 @@ long do_mmu_update( + struct vcpu *curr = current, *v = curr; + struct domain *d = v->domain, *pt_owner = d, *pg_owner; + mfn_t map_mfn = INVALID_MFN; +- bool sync_guest = false; ++ bool flush_linear_pt = false, flush_root_pt_local = false, ++ flush_root_pt_others = false; + uint32_t xsm_needed = 0; + uint32_t xsm_checked = 0; + int rc = put_old_guest_table(curr); +@@ -4155,6 +4156,8 @@ long do_mmu_update( + break; + rc = mod_l2_entry(va, l2e_from_intpte(req.val), mfn, + cmd == MMU_PT_UPDATE_PRESERVE_AD, v); ++ if ( !rc ) ++ flush_linear_pt = true; + break; + + case PGT_l3_page_table: +@@ -4162,6 +4165,8 @@ long do_mmu_update( + break; + rc = mod_l3_entry(va, l3e_from_intpte(req.val), mfn, + cmd == MMU_PT_UPDATE_PRESERVE_AD, v); ++ if ( !rc ) ++ flush_linear_pt = true; + break; + + case PGT_l4_page_table: +@@ -4169,6 +4174,8 @@ long do_mmu_update( + break; + rc = mod_l4_entry(va, l4e_from_intpte(req.val), mfn, + cmd == MMU_PT_UPDATE_PRESERVE_AD, v); ++ if ( !rc ) ++ flush_linear_pt = true; + if ( !rc && pt_owner->arch.pv.xpti ) + { + bool local_in_use = false; +@@ -4176,7 +4183,7 @@ long do_mmu_update( + if ( pagetable_get_pfn(curr->arch.guest_table) == mfn ) + { + local_in_use = true; +- get_cpu_info()->root_pgt_changed = true; ++ flush_root_pt_local = true; + } + + /* +@@ -4188,7 +4195,7 @@ long do_mmu_update( + (1 + !!(page->u.inuse.type_info & PGT_pinned) + + (pagetable_get_pfn(curr->arch.guest_table_user) == + mfn) + local_in_use) ) +- sync_guest = true; ++ flush_root_pt_others = true; + } + break; + +@@ -4290,19 +4297,61 @@ long do_mmu_update( + if ( va ) + unmap_domain_page(va); + +- if ( sync_guest ) ++ /* ++ * Perform required TLB maintenance. ++ * ++ * This logic currently depend on flush_linear_pt being a superset of the ++ * flush_root_pt_* conditions. ++ * ++ * pt_owner may not be current->domain. This may occur during ++ * construction of 32bit PV guests, or debugging of PV guests. The ++ * behaviour cannot be correct with domain unpaused. We therefore expect ++ * pt_owner->dirty_cpumask to be empty, but it is a waste of effort to ++ * explicitly check for, and exclude, this corner case. ++ * ++ * flush_linear_pt requires a FLUSH_TLB to all dirty CPUs. The flush must ++ * be performed now to maintain correct behaviour across a multicall. ++ * i.e. we cannot relax FLUSH_TLB to FLUSH_ROOT_PGTBL, given that the ++ * former is a side effect of the latter, because the resync (which is in ++ * the return-to-guest path) happens too late. ++ * ++ * flush_root_pt_* requires FLUSH_ROOT_PGTBL on either the local CPU ++ * (implies pt_owner == current->domain and current->processor set in ++ * pt_owner->dirty_cpumask), and/or all *other* dirty CPUs as there are ++ * references we can't account for locally. ++ */ ++ if ( flush_linear_pt /* || flush_root_pt_local || flush_root_pt_others */ ) + { ++ unsigned int cpu = smp_processor_id(); ++ cpumask_t *mask = pt_owner->dirty_cpumask; ++ + /* +- * Force other vCPU-s of the affected guest to pick up L4 entry +- * changes (if any). ++ * Always handle local flushing separately (if applicable), to ++ * separate the flush invocations appropriately for scope of the two ++ * flush_root_pt_* variables. + */ +- unsigned int cpu = smp_processor_id(); +- cpumask_t *mask = per_cpu(scratch_cpumask, cpu); ++ if ( likely(cpumask_test_cpu(cpu, mask)) ) ++ { ++ mask = per_cpu(scratch_cpumask, cpu); + +- cpumask_andnot(mask, pt_owner->dirty_cpumask, cpumask_of(cpu)); ++ cpumask_copy(mask, pt_owner->dirty_cpumask); ++ __cpumask_clear_cpu(cpu, mask); ++ ++ flush_local(FLUSH_TLB | ++ (flush_root_pt_local ? FLUSH_ROOT_PGTBL : 0)); ++ } ++ else ++ /* Sanity check. flush_root_pt_local implies local cpu is dirty. */ ++ ASSERT(!flush_root_pt_local); ++ ++ /* Flush the remote dirty CPUs. Does not include the local CPU. */ + if ( !cpumask_empty(mask) ) +- flush_mask(mask, FLUSH_ROOT_PGTBL); ++ flush_mask(mask, FLUSH_TLB | ++ (flush_root_pt_others ? FLUSH_ROOT_PGTBL : 0)); + } ++ else ++ /* Sanity check. flush_root_pt_* implies flush_linear_pt. */ ++ ASSERT(!flush_root_pt_local && !flush_root_pt_others); + + perfc_add(num_page_updates, i); + +-- +2.20.1 + diff --git a/xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch b/xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch deleted file mode 100644 index ce014e7..0000000 --- a/xsa286-4.12-0003-x86-mm-check-page-types-in-do_page_walk.patch +++ /dev/null @@ -1,170 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: check page types in do_page_walk() - -For page table entries read to be guaranteed valid, transiently locking -the pages and validating their types is necessary. Note that guest use -of linear page tables is intentionally not taken into account here, as -ordinary data (guest stacks) can't possibly live inside page tables. - -This is part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 9c89c7d076..c764237651 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -46,15 +46,29 @@ l2_pgentry_t *compat_idle_pg_table_l2; - - static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - { -- unsigned long mfn = pagetable_get_pfn(root); -- l4_pgentry_t *l4t, l4e; -+ mfn_t mfn = pagetable_get_mfn(root); -+ /* current's root page table can't disappear under our feet. */ -+ bool need_lock = !mfn_eq(mfn, pagetable_get_mfn(current->arch.guest_table)); -+ struct page_info *pg; -+ l4_pgentry_t l4e = l4e_empty(); - - if ( !is_canonical_address(addr) ) - return l4e_empty(); - -- l4t = map_domain_page(_mfn(mfn)); -- l4e = l4t[l4_table_offset(addr)]; -- unmap_domain_page(l4t); -+ pg = mfn_to_page(mfn); -+ if ( need_lock && !page_lock(pg) ) -+ return l4e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l4_page_table ) -+ { -+ l4_pgentry_t *l4t = map_domain_page(mfn); -+ -+ l4e = l4t[l4_table_offset(addr)]; -+ unmap_domain_page(l4t); -+ } -+ -+ if ( need_lock ) -+ page_unlock(pg); - - return l4e; - } -@@ -62,14 +76,26 @@ static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - { - l4_pgentry_t l4e = page_walk_get_l4e(root, addr); -- l3_pgentry_t *l3t, l3e; -+ mfn_t mfn = l4e_get_mfn(l4e); -+ struct page_info *pg; -+ l3_pgentry_t l3e = l3e_empty(); - - if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) - return l3e_empty(); - -- l3t = map_l3t_from_l4e(l4e); -- l3e = l3t[l3_table_offset(addr)]; -- unmap_domain_page(l3t); -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l3e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l3_page_table ) -+ { -+ l3_pgentry_t *l3t = map_domain_page(mfn); -+ -+ l3e = l3t[l3_table_offset(addr)]; -+ unmap_domain_page(l3t); -+ } -+ -+ page_unlock(pg); - - return l3e; - } -@@ -77,44 +103,67 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -- l2_pgentry_t l2e, *l2t; -- l1_pgentry_t l1e, *l1t; -- unsigned long mfn; -+ l2_pgentry_t l2e = l2e_empty(); -+ l1_pgentry_t l1e = l1e_empty(); -+ mfn_t mfn; -+ struct page_info *pg; - - if ( !is_pv_vcpu(v) ) - return NULL; - - l3e = page_walk_get_l3e(v->arch.guest_table, addr); -- mfn = l3e_get_pfn(l3e); -- if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ mfn = l3e_get_mfn(l3e); -+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - if ( (l3e_get_flags(l3e) & _PAGE_PSE) ) - { -- mfn += PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1)); -+ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1))); - goto ret; - } - -- l2t = map_domain_page(_mfn(mfn)); -- l2e = l2t[l2_table_offset(addr)]; -- unmap_domain_page(l2t); -- mfn = l2e_get_pfn(l2e); -- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return NULL; -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) -+ { -+ const l2_pgentry_t *l2t = map_domain_page(mfn); -+ -+ l2e = l2t[l2_table_offset(addr)]; -+ unmap_domain_page(l2t); -+ } -+ -+ page_unlock(pg); -+ -+ mfn = l2e_get_mfn(l2e); -+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - if ( (l2e_get_flags(l2e) & _PAGE_PSE) ) - { -- mfn += PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1)); -+ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1))); - goto ret; - } - -- l1t = map_domain_page(_mfn(mfn)); -- l1e = l1t[l1_table_offset(addr)]; -- unmap_domain_page(l1t); -- mfn = l1e_get_pfn(l1e); -- if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return NULL; -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) -+ { -+ const l1_pgentry_t *l1t = map_domain_page(mfn); -+ -+ l1e = l1t[l1_table_offset(addr)]; -+ unmap_domain_page(l1t); -+ } -+ -+ page_unlock(pg); -+ -+ mfn = l1e_get_mfn(l1e); -+ if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - - ret: -- return map_domain_page(_mfn(mfn)) + (addr & ~PAGE_MASK); -+ return map_domain_page(mfn) + (addr & ~PAGE_MASK); - } - - /* diff --git a/xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch b/xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch deleted file mode 100644 index 4e885c7..0000000 --- a/xsa286-4.12-0004-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch +++ /dev/null @@ -1,92 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using linear page tables in map_guest_l1e() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Replace the linear L2 table access by an actual page walk. - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Signed-off-by: Roger Pau MonnĂ© -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c -index 2b0dadc8da..acebf9e957 100644 ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -40,11 +40,14 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) - if ( unlikely(!__addr_ok(linear)) ) - return NULL; - -- /* Find this l1e and its enclosing l1mfn in the linear map. */ -- if ( __copy_from_user(&l2e, -- &__linear_l2_table[l2_linear_offset(linear)], -- sizeof(l2_pgentry_t)) ) -+ if ( unlikely(!(current->arch.flags & TF_kernel_mode)) ) -+ { -+ ASSERT_UNREACHABLE(); - return NULL; -+ } -+ -+ /* Find this l1e and its enclosing l1mfn. */ -+ l2e = page_walk_get_l2e(current->arch.guest_table, linear); - - /* Check flags that it will be safe to read the l1e. */ - if ( (l2e_get_flags(l2e) & (_PAGE_PRESENT | _PAGE_PSE)) != _PAGE_PRESENT ) -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index c764237651..062069a908 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -100,6 +100,34 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - return l3e; - } - -+l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) -+{ -+ l3_pgentry_t l3e = page_walk_get_l3e(root, addr); -+ mfn_t mfn = l3e_get_mfn(l3e); -+ struct page_info *pg; -+ l2_pgentry_t l2e = l2e_empty(); -+ -+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || -+ (l3e_get_flags(l3e) & _PAGE_PSE) ) -+ return l2e_empty(); -+ -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l2e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) -+ { -+ l2_pgentry_t *l2t = map_domain_page(mfn); -+ -+ l2e = l2t[l2_table_offset(addr)]; -+ unmap_domain_page(l2t); -+ } -+ -+ page_unlock(pg); -+ -+ return l2e; -+} -+ - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 845556eb6d..504f3fdc14 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -585,7 +585,9 @@ void audit_domains(void); - void make_cr3(struct vcpu *v, mfn_t mfn); - void update_cr3(struct vcpu *v); - int vcpu_destroy_pagetables(struct vcpu *); -+ - void *do_page_walk(struct vcpu *v, unsigned long addr); -+l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); - - int __sync_local_execstate(void); - diff --git a/xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch b/xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch deleted file mode 100644 index b53c6ac..0000000 --- a/xsa286-4.12-0005-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch +++ /dev/null @@ -1,172 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using linear page tables in guest_get_eff_kern_l1e() - -First of all drop guest_get_eff_l1e() entirely - there's no actual user -of it: pv_ro_page_fault() has a guest_kernel_mode() conditional around -its only call site. - -Then replace the linear L1 table access by an actual page walk. - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c -index acebf9e957..7624447246 100644 ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -59,27 +59,6 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) - } - - /* -- * Read the guest's l1e that maps this address, from the kernel-mode -- * page tables. -- */ --static l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) --{ -- struct vcpu *curr = current; -- const bool user_mode = !(curr->arch.flags & TF_kernel_mode); -- l1_pgentry_t l1e; -- -- if ( user_mode ) -- toggle_guest_pt(curr); -- -- l1e = guest_get_eff_l1e(linear); -- -- if ( user_mode ) -- toggle_guest_pt(curr); -- -- return l1e; --} -- --/* - * Map a guest's LDT page (covering the byte at @offset from start of the LDT) - * into Xen's virtual range. Returns true if the mapping changed, false - * otherwise. -diff --git a/xen/arch/x86/pv/mm.h b/xen/arch/x86/pv/mm.h -index 976209ba4c..cc4ee1affb 100644 ---- a/xen/arch/x86/pv/mm.h -+++ b/xen/arch/x86/pv/mm.h -@@ -5,19 +5,19 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn); - - int new_guest_cr3(mfn_t mfn); - --/* Read a PV guest's l1e that maps this linear address. */ --static inline l1_pgentry_t guest_get_eff_l1e(unsigned long linear) -+/* -+ * Read the guest's l1e that maps this address, from the kernel-mode -+ * page tables. -+ */ -+static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) - { -- l1_pgentry_t l1e; -+ l1_pgentry_t l1e = l1e_empty(); - - ASSERT(!paging_mode_translate(current->domain)); - ASSERT(!paging_mode_external(current->domain)); - -- if ( unlikely(!__addr_ok(linear)) || -- __copy_from_user(&l1e, -- &__linear_l1_table[l1_linear_offset(linear)], -- sizeof(l1_pgentry_t)) ) -- l1e = l1e_empty(); -+ if ( likely(__addr_ok(linear)) ) -+ l1e = page_walk_get_l1e(current->arch.guest_table, linear); - - return l1e; - } -diff --git a/xen/arch/x86/pv/ro-page-fault.c b/xen/arch/x86/pv/ro-page-fault.c -index e7a7179dda..ebac3f476a 100644 ---- a/xen/arch/x86/pv/ro-page-fault.c -+++ b/xen/arch/x86/pv/ro-page-fault.c -@@ -360,7 +360,7 @@ int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs) - bool mmio_ro; - - /* Attempt to read the PTE that maps the VA being accessed. */ -- pte = guest_get_eff_l1e(addr); -+ pte = guest_get_eff_kern_l1e(addr); - - /* We are only looking for read-only mappings */ - if ( ((l1e_get_flags(pte) & (_PAGE_PRESENT | _PAGE_RW)) != _PAGE_PRESENT) ) -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 062069a908..7dbfabf267 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -128,6 +128,62 @@ l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) - return l2e; - } - -+/* -+ * For now no "set_accessed" parameter, as all callers want it set to true. -+ * For now also no "set_dirty" parameter, as all callers deal with r/o -+ * mappings, and we don't want to set the dirty bit there (conflicts with -+ * CET-SS). However, as there are CPUs which may set the dirty bit on r/o -+ * PTEs, the logic below tolerates the bit becoming set "behind our backs". -+ */ -+l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr) -+{ -+ l2_pgentry_t l2e = page_walk_get_l2e(root, addr); -+ mfn_t mfn = l2e_get_mfn(l2e); -+ struct page_info *pg; -+ l1_pgentry_t l1e = l1e_empty(); -+ -+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || -+ (l2e_get_flags(l2e) & _PAGE_PSE) ) -+ return l1e_empty(); -+ -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l1e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) -+ { -+ l1_pgentry_t *l1t = map_domain_page(mfn); -+ -+ l1e = l1t[l1_table_offset(addr)]; -+ -+ if ( (l1e_get_flags(l1e) & (_PAGE_ACCESSED | _PAGE_PRESENT)) == -+ _PAGE_PRESENT ) -+ { -+ l1_pgentry_t ol1e = l1e; -+ -+ l1e_add_flags(l1e, _PAGE_ACCESSED); -+ /* -+ * Best effort only; with the lock held the page shouldn't -+ * change anyway, except for the dirty bit to perhaps become set. -+ */ -+ while ( cmpxchg(&l1e_get_intpte(l1t[l1_table_offset(addr)]), -+ l1e_get_intpte(ol1e), l1e_get_intpte(l1e)) != -+ l1e_get_intpte(ol1e) && -+ !(l1e_get_flags(l1e) & _PAGE_DIRTY) ) -+ { -+ l1e_add_flags(ol1e, _PAGE_DIRTY); -+ l1e_add_flags(l1e, _PAGE_DIRTY); -+ } -+ } -+ -+ unmap_domain_page(l1t); -+ } -+ -+ page_unlock(pg); -+ -+ return l1e; -+} -+ - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 504f3fdc14..0aed4c3b77 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -588,6 +588,7 @@ int vcpu_destroy_pagetables(struct vcpu *); - - void *do_page_walk(struct vcpu *v, unsigned long addr); - l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); -+l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr); - - int __sync_local_execstate(void); - diff --git a/xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch b/xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch deleted file mode 100644 index 29deb59..0000000 --- a/xsa286-4.12-0006-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch +++ /dev/null @@ -1,101 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using top level linear page tables in - {,un}map_domain_page() - -Move the page table recursion two levels down. This entails avoiding -to free the recursive mapping prematurely in free_perdomain_mappings(). - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c -index 4a07cfb18e..660bd06aaf 100644 ---- a/xen/arch/x86/domain_page.c -+++ b/xen/arch/x86/domain_page.c -@@ -65,7 +65,8 @@ void __init mapcache_override_current(struct vcpu *v) - #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) - #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) - #define MAPCACHE_L1ENT(idx) \ -- __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] -+ ((l1_pgentry_t *)(MAPCACHE_VIRT_START | \ -+ ((L2_PAGETABLE_ENTRIES - 1) << L2_PAGETABLE_SHIFT)))[idx] - - void *map_domain_page(mfn_t mfn) - { -@@ -235,6 +236,7 @@ int mapcache_domain_init(struct domain *d) - { - struct mapcache_domain *dcache = &d->arch.pv.mapcache; - unsigned int bitmap_pages; -+ int rc; - - ASSERT(is_pv_domain(d)); - -@@ -243,8 +245,10 @@ int mapcache_domain_init(struct domain *d) - return 0; - #endif - -+ BUILD_BUG_ON(MAPCACHE_VIRT_START & ((1 << L3_PAGETABLE_SHIFT) - 1)); - BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + -- 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > -+ 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) + -+ (1U << L2_PAGETABLE_SHIFT) > - MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); - bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); - dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; -@@ -253,9 +257,25 @@ int mapcache_domain_init(struct domain *d) - - spin_lock_init(&dcache->lock); - -- return create_perdomain_mapping(d, (unsigned long)dcache->inuse, -- 2 * bitmap_pages + 1, -- NIL(l1_pgentry_t *), NULL); -+ rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, -+ 2 * bitmap_pages + 1, -+ NIL(l1_pgentry_t *), NULL); -+ if ( !rc ) -+ { -+ /* -+ * Install mapping of our L2 table into its own last slot, for easy -+ * access to the L1 entries via MAPCACHE_L1ENT(). -+ */ -+ l3_pgentry_t *l3t = __map_domain_page(d->arch.perdomain_l3_pg); -+ l3_pgentry_t l3e = l3t[l3_table_offset(MAPCACHE_VIRT_END)]; -+ l2_pgentry_t *l2t = map_l2t_from_l3e(l3e); -+ -+ l2e_get_intpte(l2t[L2_PAGETABLE_ENTRIES - 1]) = l3e_get_intpte(l3e); -+ unmap_domain_page(l2t); -+ unmap_domain_page(l3t); -+ } -+ -+ return rc; - } - - int mapcache_vcpu_init(struct vcpu *v) -@@ -346,7 +366,7 @@ mfn_t domain_page_map_to_mfn(const void *ptr) - else - { - ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); -- pl1e = &__linear_l1_table[l1_linear_offset(va)]; -+ pl1e = &MAPCACHE_L1ENT(PFN_DOWN(va - MAPCACHE_VIRT_START)); - } - - return l1e_get_mfn(*pl1e); -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index b4c90bd054..d234e191a6 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -6071,6 +6071,10 @@ void free_perdomain_mappings(struct domain *d) - { - struct page_info *l1pg = l2e_get_page(l2tab[j]); - -+ /* mapcache_domain_init() installs a recursive entry. */ -+ if ( l1pg == l2pg ) -+ continue; -+ - if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) - { - l1_pgentry_t *l1tab = __map_domain_page(l1pg); diff --git a/xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch b/xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch deleted file mode 100644 index bed7b6c..0000000 --- a/xsa286-4.12-0007-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch +++ /dev/null @@ -1,106 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: restrict use of linear page tables to shadow mode code - -Other code does not require them to be set up anymore, so restrict when -to populate the respective L4 slot and reduce visibility of the -accessors. - -While with the removal of all uses the vulnerability is actually fixed, -removing the creation of the linear mapping adds an extra layer of -protection. Similarly reducing visibility of the accessors mostly -eliminates the risk of undue re-introduction of uses of the linear -mappings. - -This is (not strictly) part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index d234e191a6..51826ecb14 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1786,9 +1786,10 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, - l4t[l4_table_offset(PCI_MCFG_VIRT_START)] = - idle_pg_table[l4_table_offset(PCI_MCFG_VIRT_START)]; - -- /* Slot 258: Self linear mappings. */ -+ /* Slot 258: Self linear mappings (shadow pt only). */ - ASSERT(!mfn_eq(l4mfn, INVALID_MFN)); - l4t[l4_table_offset(LINEAR_PT_VIRT_START)] = -+ !shadow_mode_external(d) ? l4e_empty() : - l4e_from_mfn(l4mfn, __PAGE_HYPERVISOR_RW); - - /* Slot 259: Shadow linear mappings (if applicable) .*/ -diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h -index 580ef3e29e..f082f02670 100644 ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -135,6 +135,15 @@ enum { - # define GUEST_PTE_SIZE 4 - #endif - -+/* Where to find each level of the linear mapping */ -+#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) -+#define __linear_l2_table \ -+ ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) -+#define __linear_l3_table \ -+ ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) -+#define __linear_l4_table \ -+ ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) -+ - /****************************************************************************** - * Auditing routines - */ -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 7dbfabf267..577938bc17 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -833,9 +833,6 @@ void __init paging_init(void) - - machine_to_phys_mapping_valid = 1; - -- /* Set up linear page table mapping. */ -- l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], -- l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR_RW)); - return; - - nomem: -diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h -index feeb456bee..01a56dcab0 100644 ---- a/xen/include/asm-x86/config.h -+++ b/xen/include/asm-x86/config.h -@@ -193,7 +193,7 @@ extern unsigned char boot_edid_info[128]; - */ - #define PCI_MCFG_VIRT_START (PML4_ADDR(257)) - #define PCI_MCFG_VIRT_END (PCI_MCFG_VIRT_START + PML4_ENTRY_BYTES) --/* Slot 258: linear page table (guest table). */ -+/* Slot 258: linear page table (monitor table, HVM only). */ - #define LINEAR_PT_VIRT_START (PML4_ADDR(258)) - #define LINEAR_PT_VIRT_END (LINEAR_PT_VIRT_START + PML4_ENTRY_BYTES) - /* Slot 259: linear page table (shadow table). */ -diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h -index c1e92937c0..e72c277b9f 100644 ---- a/xen/include/asm-x86/page.h -+++ b/xen/include/asm-x86/page.h -@@ -274,19 +274,6 @@ void copy_page_sse2(void *, const void *); - #define vmap_to_mfn(va) _mfn(l1e_get_pfn(*virt_to_xen_l1e((unsigned long)(va)))) - #define vmap_to_page(va) mfn_to_page(vmap_to_mfn(va)) - --#endif /* !defined(__ASSEMBLY__) */ -- --/* Where to find each level of the linear mapping */ --#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) --#define __linear_l2_table \ -- ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) --#define __linear_l3_table \ -- ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) --#define __linear_l4_table \ -- ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) -- -- --#ifndef __ASSEMBLY__ - extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES]; - extern l2_pgentry_t *compat_idle_pg_table_l2; - extern unsigned int m2p_compat_vstart;