Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c54bf4880 | ||
|
|
ed0d7278e6 | ||
|
|
1eef9003b1 | ||
|
|
01153e7a78 |
33 changed files with 29 additions and 3559 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz
|
|||
pciutils-2.2.9.tar.bz2
|
||||
zlib-1.2.3.tar.gz
|
||||
polarssl-1.1.4-gpl.tgz
|
||||
/xen-4.17.2.tar.gz
|
||||
/xen-4.17.4.tar.gz
|
||||
|
|
|
|||
2
sources
2
sources
|
|
@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77
|
|||
SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e
|
||||
SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad
|
||||
SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5
|
||||
SHA512 (xen-4.17.2.tar.gz) = 0bc475483676e4aa27735695f9a8d2821059e7a55984adb8a29badb5c09a4e7cf8ea29cbc9691be616cc0d7a5ee6b6dacc59ba29c2b16e0919ebdf7dfc54201a
|
||||
SHA512 (xen-4.17.4.tar.gz) = 50dc2efd26e48131afdbc8efb1ca90154a84fe5fba5a6bc81d9801a3c13aebea91c8211872b5c69ce2773644f2c935c31cc56584a05b10b81e8728282d89eb84
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
--- xen-4.16.0/Config.mk.orig 2021-11-30 11:42:42.000000000 +0000
|
||||
+++ xen-4.16.0/Config.mk 2022-01-24 20:25:16.687125822 +0000
|
||||
@@ -186,6 +186,7 @@
|
||||
$(call cc-option-add,CFLAGS,CC,-Wdeclaration-after-statement)
|
||||
|
||||
$(call cc-option-add,CFLAGS,CC,-Wno-unused-but-set-variable)
|
||||
$(call cc-option-add,CFLAGS,CC,-Wno-unused-local-typedefs)
|
||||
+$(call cc-option-add,CFLAGS,CC,-Wno-error=array-bounds)
|
||||
|
|
|
|||
86
xen.spec
86
xen.spec
|
|
@ -54,8 +54,8 @@
|
|||
|
||||
Summary: Xen is a virtual machine monitor
|
||||
Name: xen
|
||||
Version: 4.17.2
|
||||
Release: 5%{?dist}
|
||||
Version: 4.17.4
|
||||
Release: 1%{?dist}
|
||||
License: GPLv2+ and LGPLv2+ and BSD
|
||||
URL: http://xen.org/
|
||||
Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz
|
||||
|
|
@ -112,35 +112,6 @@ Patch46: xen.efi.build.patch
|
|||
Patch47: xen.gcc13.fixes.patch
|
||||
Patch49: xen.python3.12.patch
|
||||
Patch50: xen.ocaml5.fixes.patch
|
||||
Patch51: xsa437.patch
|
||||
Patch52: xsa438-4.17.patch
|
||||
Patch53: xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch
|
||||
Patch54: xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch
|
||||
Patch55: xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch
|
||||
Patch56: xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch
|
||||
Patch57: xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch
|
||||
Patch58: xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch
|
||||
Patch59: xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch
|
||||
Patch60: xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch
|
||||
Patch61: xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch
|
||||
Patch62: xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch
|
||||
Patch63: xsa440-4.17.patch
|
||||
Patch64: xsa442-4.17.patch
|
||||
Patch65: xsa443-4.17-01.patch
|
||||
Patch66: xsa443-4.17-02.patch
|
||||
Patch67: xsa443-4.17-03.patch
|
||||
Patch68: xsa443-4.17-04.patch
|
||||
Patch69: xsa443-4.17-05.patch
|
||||
Patch70: xsa443-4.17-06.patch
|
||||
Patch71: xsa443-4.17-07.patch
|
||||
Patch72: xsa443-4.17-08.patch
|
||||
Patch73: xsa443-4.17-09.patch
|
||||
Patch74: xsa443-4.17-10.patch
|
||||
Patch75: xsa443-4.17-11.patch
|
||||
Patch76: xsa444-4.17-1.patch
|
||||
Patch77: xsa444-4.17-2.patch
|
||||
Patch78: xsa445-4.17.patch
|
||||
Patch79: xsa446.patch
|
||||
|
||||
|
||||
%if %build_qemutrad
|
||||
|
|
@ -357,35 +328,6 @@ manage Xen virtual machines.
|
|||
%if "%dist" != ".fc38"
|
||||
%patch 50 -p1
|
||||
%endif
|
||||
%patch 51 -p1
|
||||
%patch 52 -p1
|
||||
%patch 53 -p1
|
||||
%patch 54 -p1
|
||||
%patch 55 -p1
|
||||
%patch 56 -p1
|
||||
%patch 57 -p1
|
||||
%patch 58 -p1
|
||||
%patch 59 -p1
|
||||
%patch 60 -p1
|
||||
%patch 61 -p1
|
||||
%patch 62 -p1
|
||||
%patch 63 -p1
|
||||
%patch 64 -p1
|
||||
%patch 65 -p1
|
||||
%patch 66 -p1
|
||||
%patch 67 -p1
|
||||
%patch 68 -p1
|
||||
%patch 69 -p1
|
||||
%patch 70 -p1
|
||||
%patch 71 -p1
|
||||
%patch 72 -p1
|
||||
%patch 73 -p1
|
||||
%patch 74 -p1
|
||||
%patch 75 -p1
|
||||
%patch 76 -p1
|
||||
%patch 77 -p1
|
||||
%patch 78 -p1
|
||||
%patch 79 -p1
|
||||
|
||||
# qemu-xen-traditional patches
|
||||
pushd tools/qemu-xen-traditional
|
||||
|
|
@ -993,6 +935,30 @@ fi
|
|||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Apr 09 2024 Michael Young <m.a.young@durham.ac.uk> - 4.17.4-1
|
||||
- x86: Native Branch History Injection [XSA-456, CVE-2024-2201]
|
||||
- update to xen 4.17.4, remove patches now included upstream
|
||||
rebase xen.gcc12.fixes.patch
|
||||
- x86 HVM hypercalls may trigger Xen bug check [XSA-454, CVE-2023-46842]
|
||||
- x86: Incorrect logic for BTC/SRSO mitigations [XSA-455, CVE-2024-31142]
|
||||
|
||||
* Thu Mar 14 2024 Michael Young <m.a.young@durham.ac.uk> - 4.17.2-8
|
||||
- x86: Register File Data Sampling [XSA-452, CVE-2023-28746]
|
||||
- GhostRace: Speculative Race Conditions [XSA-453, CVE-2024-2193]
|
||||
- additional patches so above applies cleanly
|
||||
|
||||
* Tue Feb 27 2024 Michael Young <m.a.young@durham.ac.uk> - 4.17.2-7
|
||||
- x86: shadow stack vs exceptions from emulation stubs - [XSA-451,
|
||||
CVE-2023-46841] (#2266326)
|
||||
|
||||
* Tue Jan 30 2024 Michael Young <m.a.young@durham.ac.uk> - 4.17.2-6
|
||||
- arm32: The cache may not be properly cleaned/invalidated (take two)
|
||||
[XSA-447, CVE-2023-46837]
|
||||
- pci: phantom functions assigned to incorrect contexts [XSA-449,
|
||||
CVE-2023-46839]
|
||||
- VT-d: Failure to quarantine devices in !HVM build [XSA-450,
|
||||
CVE-2023-46840]
|
||||
|
||||
* Tue Nov 14 2023 Michael Young <m.a.young@durham.ac.uk> - 4.17.2-5
|
||||
- x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445,
|
||||
CVE-2023-46835]
|
||||
|
|
|
|||
110
xsa437.patch
110
xsa437.patch
|
|
@ -1,110 +0,0 @@
|
|||
From 7fac5971340a13ca9458195305bcfe14df2e52d2 Mon Sep 17 00:00:00 2001
|
||||
From: Stefano Stabellini <stefano.stabellini@amd.com>
|
||||
Date: Thu, 17 Aug 2023 13:41:35 +0100
|
||||
Subject: [PATCH] xen/arm: page: Handle cache flush of an element at the top of
|
||||
the address space
|
||||
|
||||
The region that needs to be cleaned/invalidated may be at the top
|
||||
of the address space. This means that 'end' (i.e. 'p + size') will
|
||||
be 0 and therefore nothing will be cleaned/invalidated as the check
|
||||
in the loop will always be false.
|
||||
|
||||
On Arm64, we only support we only support up to 48-bit Virtual
|
||||
address space. So this is not a concern there. However, for 32-bit,
|
||||
the mapcache is using the last 2GB of the address space. Therefore
|
||||
we may not clean/invalidate properly some pages. This could lead
|
||||
to memory corruption or data leakage (the scrubbed value may
|
||||
still sit in the cache when the guest could read directly the memory
|
||||
and therefore read the old content).
|
||||
|
||||
Rework invalidate_dcache_va_range(), clean_dcache_va_range(),
|
||||
clean_and_invalidate_dcache_va_range() to handle a cache flush
|
||||
with an element at the top of the address space.
|
||||
|
||||
This is CVE-2023-34321 / XSA-437.
|
||||
|
||||
Reported-by: Julien Grall <jgrall@amazon.com>
|
||||
Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com>
|
||||
Signed-off-by: Julien Grall <jgrall@amazon.com>
|
||||
Acked-by: Bertrand Marquis <bertrand.marquis@arm.com>
|
||||
|
||||
---
|
||||
xen/arch/arm/include/asm/page.h | 33 ++++++++++++++++++++-------------
|
||||
1 file changed, 20 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/arm/include/asm/page.h b/xen/arch/arm/include/asm/page.h
|
||||
index e7cd62190c7f..d7fe770a5e49 100644
|
||||
--- a/xen/arch/arm/include/asm/page.h
|
||||
+++ b/xen/arch/arm/include/asm/page.h
|
||||
@@ -160,26 +160,25 @@ static inline size_t read_dcache_line_bytes(void)
|
||||
|
||||
static inline int invalidate_dcache_va_range(const void *p, unsigned long size)
|
||||
{
|
||||
- const void *end = p + size;
|
||||
size_t cacheline_mask = dcache_line_bytes - 1;
|
||||
|
||||
dsb(sy); /* So the CPU issues all writes to the range */
|
||||
|
||||
if ( (uintptr_t)p & cacheline_mask )
|
||||
{
|
||||
+ size -= dcache_line_bytes - ((uintptr_t)p & cacheline_mask);
|
||||
p = (void *)((uintptr_t)p & ~cacheline_mask);
|
||||
asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p));
|
||||
p += dcache_line_bytes;
|
||||
}
|
||||
- if ( (uintptr_t)end & cacheline_mask )
|
||||
- {
|
||||
- end = (void *)((uintptr_t)end & ~cacheline_mask);
|
||||
- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (end));
|
||||
- }
|
||||
|
||||
- for ( ; p < end; p += dcache_line_bytes )
|
||||
+ for ( ; size >= dcache_line_bytes;
|
||||
+ p += dcache_line_bytes, size -= dcache_line_bytes )
|
||||
asm volatile (__invalidate_dcache_one(0) : : "r" (p));
|
||||
|
||||
+ if ( size > 0 )
|
||||
+ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p));
|
||||
+
|
||||
dsb(sy); /* So we know the flushes happen before continuing */
|
||||
|
||||
return 0;
|
||||
@@ -187,10 +186,14 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size)
|
||||
|
||||
static inline int clean_dcache_va_range(const void *p, unsigned long size)
|
||||
{
|
||||
- const void *end = p + size;
|
||||
+ size_t cacheline_mask = dcache_line_bytes - 1;
|
||||
+
|
||||
dsb(sy); /* So the CPU issues all writes to the range */
|
||||
- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1));
|
||||
- for ( ; p < end; p += dcache_line_bytes )
|
||||
+ size += (uintptr_t)p & cacheline_mask;
|
||||
+ size = (size + cacheline_mask) & ~cacheline_mask;
|
||||
+ p = (void *)((uintptr_t)p & ~cacheline_mask);
|
||||
+ for ( ; size >= dcache_line_bytes;
|
||||
+ p += dcache_line_bytes, size -= dcache_line_bytes )
|
||||
asm volatile (__clean_dcache_one(0) : : "r" (p));
|
||||
dsb(sy); /* So we know the flushes happen before continuing */
|
||||
/* ARM callers assume that dcache_* functions cannot fail. */
|
||||
@@ -200,10 +203,14 @@ static inline int clean_dcache_va_range(const void *p, unsigned long size)
|
||||
static inline int clean_and_invalidate_dcache_va_range
|
||||
(const void *p, unsigned long size)
|
||||
{
|
||||
- const void *end = p + size;
|
||||
+ size_t cacheline_mask = dcache_line_bytes - 1;
|
||||
+
|
||||
dsb(sy); /* So the CPU issues all writes to the range */
|
||||
- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1));
|
||||
- for ( ; p < end; p += dcache_line_bytes )
|
||||
+ size += (uintptr_t)p & cacheline_mask;
|
||||
+ size = (size + cacheline_mask) & ~cacheline_mask;
|
||||
+ p = (void *)((uintptr_t)p & ~cacheline_mask);
|
||||
+ for ( ; size >= dcache_line_bytes;
|
||||
+ p += dcache_line_bytes, size -= dcache_line_bytes )
|
||||
asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p));
|
||||
dsb(sy); /* So we know the flushes happen before continuing */
|
||||
/* ARM callers assume that dcache_* functions cannot fail. */
|
||||
--
|
||||
2.40.1
|
||||
|
||||
|
|
@ -1,416 +0,0 @@
|
|||
From: Jan Beulich <jbeulich@suse.com>
|
||||
Subject: x86/shadow: defer releasing of PV's top-level shadow reference
|
||||
|
||||
sh_set_toplevel_shadow() re-pinning the top-level shadow we may be
|
||||
running on is not enough (and at the same time unnecessary when the
|
||||
shadow isn't what we're running on): That shadow becomes eligible for
|
||||
blowing away (from e.g. shadow_prealloc()) immediately after the
|
||||
paging lock was dropped. Yet it needs to remain valid until the actual
|
||||
page table switch occurred.
|
||||
|
||||
Propagate up the call chain the shadow entry that needs releasing
|
||||
eventually, and carry out the release immediately after switching page
|
||||
tables. Handle update_cr3() failures by switching to idle pagetables.
|
||||
Note that various further uses of update_cr3() are HVM-only or only act
|
||||
on paused vCPU-s, in which case sh_set_toplevel_shadow() will not defer
|
||||
releasing of the reference.
|
||||
|
||||
While changing the update_cr3() hook, also convert the "do_locking"
|
||||
parameter to boolean.
|
||||
|
||||
This is CVE-2023-34322 / XSA-438.
|
||||
|
||||
Reported-by: Tim Deegan <tim@xen.org>
|
||||
Signed-off-by: Jan Beulich <jbeulich@suse.com>
|
||||
Reviewed-by: George Dunlap <george.dunlap@cloud.com>
|
||||
|
||||
--- a/xen/arch/x86/include/asm/mm.h
|
||||
+++ b/xen/arch/x86/include/asm/mm.h
|
||||
@@ -552,7 +552,7 @@ void audit_domains(void);
|
||||
#endif
|
||||
|
||||
void make_cr3(struct vcpu *v, mfn_t mfn);
|
||||
-void update_cr3(struct vcpu *v);
|
||||
+pagetable_t update_cr3(struct vcpu *v);
|
||||
int vcpu_destroy_pagetables(struct vcpu *);
|
||||
void *do_page_walk(struct vcpu *v, unsigned long addr);
|
||||
|
||||
--- a/xen/arch/x86/include/asm/paging.h
|
||||
+++ b/xen/arch/x86/include/asm/paging.h
|
||||
@@ -138,7 +138,7 @@ struct paging_mode {
|
||||
paddr_t ga, uint32_t *pfec,
|
||||
unsigned int *page_order);
|
||||
#endif
|
||||
- void (*update_cr3 )(struct vcpu *v, int do_locking,
|
||||
+ pagetable_t (*update_cr3 )(struct vcpu *v, bool do_locking,
|
||||
bool noflush);
|
||||
void (*update_paging_modes )(struct vcpu *v);
|
||||
bool (*flush_tlb )(const unsigned long *vcpu_bitmap);
|
||||
@@ -310,9 +310,9 @@ static inline unsigned long paging_ga_to
|
||||
/* Update all the things that are derived from the guest's CR3.
|
||||
* Called when the guest changes CR3; the caller can then use v->arch.cr3
|
||||
* as the value to load into the host CR3 to schedule this vcpu */
|
||||
-static inline void paging_update_cr3(struct vcpu *v, bool noflush)
|
||||
+static inline pagetable_t paging_update_cr3(struct vcpu *v, bool noflush)
|
||||
{
|
||||
- paging_get_hostmode(v)->update_cr3(v, 1, noflush);
|
||||
+ return paging_get_hostmode(v)->update_cr3(v, 1, noflush);
|
||||
}
|
||||
|
||||
/* Update all the things that are derived from the guest's CR0/CR3/CR4.
|
||||
--- a/xen/arch/x86/include/asm/shadow.h
|
||||
+++ b/xen/arch/x86/include/asm/shadow.h
|
||||
@@ -99,6 +99,9 @@ int shadow_set_allocation(struct domain
|
||||
|
||||
int shadow_get_allocation_bytes(struct domain *d, uint64_t *size);
|
||||
|
||||
+/* Helper to invoke for deferred releasing of a top-level shadow's reference. */
|
||||
+void shadow_put_top_level(struct domain *d, pagetable_t old);
|
||||
+
|
||||
#else /* !CONFIG_SHADOW_PAGING */
|
||||
|
||||
#define shadow_vcpu_teardown(v) ASSERT(is_pv_vcpu(v))
|
||||
@@ -121,6 +124,11 @@ static inline void shadow_prepare_page_t
|
||||
|
||||
static inline void shadow_blow_tables_per_domain(struct domain *d) {}
|
||||
|
||||
+static inline void shadow_put_top_level(struct domain *d, pagetable_t old)
|
||||
+{
|
||||
+ ASSERT_UNREACHABLE();
|
||||
+}
|
||||
+
|
||||
static inline int shadow_domctl(struct domain *d,
|
||||
struct xen_domctl_shadow_op *sc,
|
||||
XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
|
||||
--- a/xen/arch/x86/mm/hap/hap.c
|
||||
+++ b/xen/arch/x86/mm/hap/hap.c
|
||||
@@ -739,11 +739,13 @@ static bool cf_check hap_invlpg(struct v
|
||||
return 1;
|
||||
}
|
||||
|
||||
-static void cf_check hap_update_cr3(
|
||||
- struct vcpu *v, int do_locking, bool noflush)
|
||||
+static pagetable_t cf_check hap_update_cr3(
|
||||
+ struct vcpu *v, bool do_locking, bool noflush)
|
||||
{
|
||||
v->arch.hvm.hw_cr[3] = v->arch.hvm.guest_cr[3];
|
||||
hvm_update_guest_cr3(v, noflush);
|
||||
+
|
||||
+ return pagetable_null();
|
||||
}
|
||||
|
||||
static bool flush_vcpu(const struct vcpu *v, const unsigned long *vcpu_bitmap)
|
||||
--- a/xen/arch/x86/mm/shadow/common.c
|
||||
+++ b/xen/arch/x86/mm/shadow/common.c
|
||||
@@ -2590,13 +2590,13 @@ void cf_check shadow_update_paging_modes
|
||||
}
|
||||
|
||||
/* Set up the top-level shadow and install it in slot 'slot' of shadow_table */
|
||||
-void sh_set_toplevel_shadow(struct vcpu *v,
|
||||
- unsigned int slot,
|
||||
- mfn_t gmfn,
|
||||
- unsigned int root_type,
|
||||
- mfn_t (*make_shadow)(struct vcpu *v,
|
||||
- mfn_t gmfn,
|
||||
- uint32_t shadow_type))
|
||||
+pagetable_t sh_set_toplevel_shadow(struct vcpu *v,
|
||||
+ unsigned int slot,
|
||||
+ mfn_t gmfn,
|
||||
+ unsigned int root_type,
|
||||
+ mfn_t (*make_shadow)(struct vcpu *v,
|
||||
+ mfn_t gmfn,
|
||||
+ uint32_t shadow_type))
|
||||
{
|
||||
mfn_t smfn;
|
||||
pagetable_t old_entry, new_entry;
|
||||
@@ -2653,20 +2653,37 @@ void sh_set_toplevel_shadow(struct vcpu
|
||||
mfn_x(gmfn), mfn_x(pagetable_get_mfn(new_entry)));
|
||||
v->arch.paging.shadow.shadow_table[slot] = new_entry;
|
||||
|
||||
- /* Decrement the refcount of the old contents of this slot */
|
||||
- if ( !pagetable_is_null(old_entry) )
|
||||
+ /*
|
||||
+ * Decrement the refcount of the old contents of this slot, unless
|
||||
+ * we're still running on that shadow - in that case it'll need holding
|
||||
+ * on to until the actual page table switch did occur.
|
||||
+ */
|
||||
+ if ( !pagetable_is_null(old_entry) && (v != current || !is_pv_domain(d)) )
|
||||
{
|
||||
- mfn_t old_smfn = pagetable_get_mfn(old_entry);
|
||||
- /* Need to repin the old toplevel shadow if it's been unpinned
|
||||
- * by shadow_prealloc(): in PV mode we're still running on this
|
||||
- * shadow and it's not safe to free it yet. */
|
||||
- if ( !mfn_to_page(old_smfn)->u.sh.pinned && !sh_pin(d, old_smfn) )
|
||||
- {
|
||||
- printk(XENLOG_G_ERR "can't re-pin %"PRI_mfn"\n", mfn_x(old_smfn));
|
||||
- domain_crash(d);
|
||||
- }
|
||||
- sh_put_ref(d, old_smfn, 0);
|
||||
+ sh_put_ref(d, pagetable_get_mfn(old_entry), 0);
|
||||
+ old_entry = pagetable_null();
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * 2- and 3-level shadow mode is used for HVM only. Therefore we never run
|
||||
+ * on such a shadow, so only call sites requesting an L4 shadow need to pay
|
||||
+ * attention to the returned value.
|
||||
+ */
|
||||
+ ASSERT(pagetable_is_null(old_entry) || root_type == SH_type_l4_64_shadow);
|
||||
+
|
||||
+ return old_entry;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Helper invoked when releasing of a top-level shadow's reference was
|
||||
+ * deferred in sh_set_toplevel_shadow() above.
|
||||
+ */
|
||||
+void shadow_put_top_level(struct domain *d, pagetable_t old_entry)
|
||||
+{
|
||||
+ ASSERT(!pagetable_is_null(old_entry));
|
||||
+ paging_lock(d);
|
||||
+ sh_put_ref(d, pagetable_get_mfn(old_entry), 0);
|
||||
+ paging_unlock(d);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
--- a/xen/arch/x86/mm/shadow/multi.c
|
||||
+++ b/xen/arch/x86/mm/shadow/multi.c
|
||||
@@ -3224,7 +3224,8 @@ static void cf_check sh_detach_old_table
|
||||
}
|
||||
}
|
||||
|
||||
-static void cf_check sh_update_cr3(struct vcpu *v, int do_locking, bool noflush)
|
||||
+static pagetable_t cf_check sh_update_cr3(struct vcpu *v, bool do_locking,
|
||||
+ bool noflush)
|
||||
/* Updates vcpu->arch.cr3 after the guest has changed CR3.
|
||||
* Paravirtual guests should set v->arch.guest_table (and guest_table_user,
|
||||
* if appropriate).
|
||||
@@ -3238,6 +3239,7 @@ static void cf_check sh_update_cr3(struc
|
||||
{
|
||||
struct domain *d = v->domain;
|
||||
mfn_t gmfn;
|
||||
+ pagetable_t old_entry = pagetable_null();
|
||||
#if GUEST_PAGING_LEVELS == 3
|
||||
const guest_l3e_t *gl3e;
|
||||
unsigned int i, guest_idx;
|
||||
@@ -3247,7 +3249,7 @@ static void cf_check sh_update_cr3(struc
|
||||
if ( !is_hvm_domain(d) && !v->is_initialised )
|
||||
{
|
||||
ASSERT(v->arch.cr3 == 0);
|
||||
- return;
|
||||
+ return old_entry;
|
||||
}
|
||||
|
||||
if ( do_locking ) paging_lock(v->domain);
|
||||
@@ -3320,11 +3322,12 @@ static void cf_check sh_update_cr3(struc
|
||||
#if GUEST_PAGING_LEVELS == 4
|
||||
if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 )
|
||||
guest_flush_tlb_mask(d, d->dirty_cpumask);
|
||||
- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow);
|
||||
+ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow,
|
||||
+ sh_make_shadow);
|
||||
if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) )
|
||||
{
|
||||
ASSERT(d->is_dying || d->is_shutting_down);
|
||||
- return;
|
||||
+ return old_entry;
|
||||
}
|
||||
if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) )
|
||||
{
|
||||
@@ -3368,24 +3371,30 @@ static void cf_check sh_update_cr3(struc
|
||||
gl2gfn = guest_l3e_get_gfn(gl3e[i]);
|
||||
gl2mfn = get_gfn_query_unlocked(d, gfn_x(gl2gfn), &p2mt);
|
||||
if ( p2m_is_ram(p2mt) )
|
||||
- sh_set_toplevel_shadow(v, i, gl2mfn, SH_type_l2_shadow,
|
||||
- sh_make_shadow);
|
||||
+ old_entry = sh_set_toplevel_shadow(v, i, gl2mfn,
|
||||
+ SH_type_l2_shadow,
|
||||
+ sh_make_shadow);
|
||||
else
|
||||
- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0,
|
||||
- sh_make_shadow);
|
||||
+ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0,
|
||||
+ sh_make_shadow);
|
||||
}
|
||||
else
|
||||
- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, sh_make_shadow);
|
||||
+ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0,
|
||||
+ sh_make_shadow);
|
||||
+
|
||||
+ ASSERT(pagetable_is_null(old_entry));
|
||||
}
|
||||
}
|
||||
#elif GUEST_PAGING_LEVELS == 2
|
||||
if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 )
|
||||
guest_flush_tlb_mask(d, d->dirty_cpumask);
|
||||
- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow);
|
||||
+ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow,
|
||||
+ sh_make_shadow);
|
||||
+ ASSERT(pagetable_is_null(old_entry));
|
||||
if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) )
|
||||
{
|
||||
ASSERT(d->is_dying || d->is_shutting_down);
|
||||
- return;
|
||||
+ return old_entry;
|
||||
}
|
||||
#else
|
||||
#error This should never happen
|
||||
@@ -3473,6 +3482,8 @@ static void cf_check sh_update_cr3(struc
|
||||
|
||||
/* Release the lock, if we took it (otherwise it's the caller's problem) */
|
||||
if ( do_locking ) paging_unlock(v->domain);
|
||||
+
|
||||
+ return old_entry;
|
||||
}
|
||||
|
||||
|
||||
--- a/xen/arch/x86/mm/shadow/none.c
|
||||
+++ b/xen/arch/x86/mm/shadow/none.c
|
||||
@@ -52,9 +52,11 @@ static unsigned long cf_check _gva_to_gf
|
||||
}
|
||||
#endif
|
||||
|
||||
-static void cf_check _update_cr3(struct vcpu *v, int do_locking, bool noflush)
|
||||
+static pagetable_t cf_check _update_cr3(struct vcpu *v, bool do_locking,
|
||||
+ bool noflush)
|
||||
{
|
||||
ASSERT_UNREACHABLE();
|
||||
+ return pagetable_null();
|
||||
}
|
||||
|
||||
static void cf_check _update_paging_modes(struct vcpu *v)
|
||||
--- a/xen/arch/x86/mm/shadow/private.h
|
||||
+++ b/xen/arch/x86/mm/shadow/private.h
|
||||
@@ -391,13 +391,13 @@ mfn_t shadow_alloc(struct domain *d,
|
||||
void shadow_free(struct domain *d, mfn_t smfn);
|
||||
|
||||
/* Set up the top-level shadow and install it in slot 'slot' of shadow_table */
|
||||
-void sh_set_toplevel_shadow(struct vcpu *v,
|
||||
- unsigned int slot,
|
||||
- mfn_t gmfn,
|
||||
- unsigned int root_type,
|
||||
- mfn_t (*make_shadow)(struct vcpu *v,
|
||||
- mfn_t gmfn,
|
||||
- uint32_t shadow_type));
|
||||
+pagetable_t sh_set_toplevel_shadow(struct vcpu *v,
|
||||
+ unsigned int slot,
|
||||
+ mfn_t gmfn,
|
||||
+ unsigned int root_type,
|
||||
+ mfn_t (*make_shadow)(struct vcpu *v,
|
||||
+ mfn_t gmfn,
|
||||
+ uint32_t shadow_type));
|
||||
|
||||
/* Update the shadows in response to a pagetable write from Xen */
|
||||
int sh_validate_guest_entry(struct vcpu *v, mfn_t gmfn, void *entry, u32 size);
|
||||
--- a/xen/arch/x86/mm.c
|
||||
+++ b/xen/arch/x86/mm.c
|
||||
@@ -567,15 +567,12 @@ void write_ptbase(struct vcpu *v)
|
||||
*
|
||||
* Update ref counts to shadow tables appropriately.
|
||||
*/
|
||||
-void update_cr3(struct vcpu *v)
|
||||
+pagetable_t update_cr3(struct vcpu *v)
|
||||
{
|
||||
mfn_t cr3_mfn;
|
||||
|
||||
if ( paging_mode_enabled(v->domain) )
|
||||
- {
|
||||
- paging_update_cr3(v, false);
|
||||
- return;
|
||||
- }
|
||||
+ return paging_update_cr3(v, false);
|
||||
|
||||
if ( !(v->arch.flags & TF_kernel_mode) )
|
||||
cr3_mfn = pagetable_get_mfn(v->arch.guest_table_user);
|
||||
@@ -583,6 +580,8 @@ void update_cr3(struct vcpu *v)
|
||||
cr3_mfn = pagetable_get_mfn(v->arch.guest_table);
|
||||
|
||||
make_cr3(v, cr3_mfn);
|
||||
+
|
||||
+ return pagetable_null();
|
||||
}
|
||||
|
||||
static inline void set_tlbflush_timestamp(struct page_info *page)
|
||||
@@ -3285,6 +3284,7 @@ int new_guest_cr3(mfn_t mfn)
|
||||
struct domain *d = curr->domain;
|
||||
int rc;
|
||||
mfn_t old_base_mfn;
|
||||
+ pagetable_t old_shadow;
|
||||
|
||||
if ( is_pv_32bit_domain(d) )
|
||||
{
|
||||
@@ -3352,9 +3352,22 @@ int new_guest_cr3(mfn_t mfn)
|
||||
if ( !VM_ASSIST(d, m2p_strict) )
|
||||
fill_ro_mpt(mfn);
|
||||
curr->arch.guest_table = pagetable_from_mfn(mfn);
|
||||
- update_cr3(curr);
|
||||
+ old_shadow = update_cr3(curr);
|
||||
+
|
||||
+ /*
|
||||
+ * In shadow mode update_cr3() can fail, in which case here we're still
|
||||
+ * running on the prior top-level shadow (which we're about to release).
|
||||
+ * Switch to the idle page tables in such an event; the guest will have
|
||||
+ * been crashed already.
|
||||
+ */
|
||||
+ if ( likely(!mfn_eq(pagetable_get_mfn(old_shadow),
|
||||
+ maddr_to_mfn(curr->arch.cr3 & ~X86_CR3_NOFLUSH))) )
|
||||
+ write_ptbase(curr);
|
||||
+ else
|
||||
+ write_ptbase(idle_vcpu[curr->processor]);
|
||||
|
||||
- write_ptbase(curr);
|
||||
+ if ( !pagetable_is_null(old_shadow) )
|
||||
+ shadow_put_top_level(d, old_shadow);
|
||||
|
||||
if ( likely(mfn_x(old_base_mfn) != 0) )
|
||||
{
|
||||
--- a/xen/arch/x86/pv/domain.c
|
||||
+++ b/xen/arch/x86/pv/domain.c
|
||||
@@ -424,10 +424,13 @@ bool __init xpti_pcid_enabled(void)
|
||||
|
||||
static void _toggle_guest_pt(struct vcpu *v)
|
||||
{
|
||||
+ bool guest_update;
|
||||
+ pagetable_t old_shadow;
|
||||
unsigned long cr3;
|
||||
|
||||
v->arch.flags ^= TF_kernel_mode;
|
||||
- update_cr3(v);
|
||||
+ guest_update = v->arch.flags & TF_kernel_mode;
|
||||
+ old_shadow = update_cr3(v);
|
||||
|
||||
/*
|
||||
* Don't flush user global mappings from the TLB. Don't tick TLB clock.
|
||||
@@ -436,13 +439,31 @@ static void _toggle_guest_pt(struct vcpu
|
||||
* TLB flush (for just the incoming PCID), as the top level page table may
|
||||
* have changed behind our backs. To be on the safe side, suppress the
|
||||
* no-flush unconditionally in this case.
|
||||
+ *
|
||||
+ * Furthermore in shadow mode update_cr3() can fail, in which case here
|
||||
+ * we're still running on the prior top-level shadow (which we're about
|
||||
+ * to release). Switch to the idle page tables in such an event; the
|
||||
+ * guest will have been crashed already.
|
||||
*/
|
||||
cr3 = v->arch.cr3;
|
||||
if ( shadow_mode_enabled(v->domain) )
|
||||
+ {
|
||||
cr3 &= ~X86_CR3_NOFLUSH;
|
||||
+
|
||||
+ if ( unlikely(mfn_eq(pagetable_get_mfn(old_shadow),
|
||||
+ maddr_to_mfn(cr3))) )
|
||||
+ {
|
||||
+ cr3 = idle_vcpu[v->processor]->arch.cr3;
|
||||
+ /* Also suppress runstate/time area updates below. */
|
||||
+ guest_update = false;
|
||||
+ }
|
||||
+ }
|
||||
write_cr3(cr3);
|
||||
|
||||
- if ( !(v->arch.flags & TF_kernel_mode) )
|
||||
+ if ( !pagetable_is_null(old_shadow) )
|
||||
+ shadow_put_top_level(v->domain, old_shadow);
|
||||
+
|
||||
+ if ( !guest_update )
|
||||
return;
|
||||
|
||||
if ( v->arch.pv.need_update_runstate_area && update_runstate_area(v) )
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
From d2d2dcae879c6cc05227c9620f0a772f35fe6886 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Jan Beulich <jbeulich@suse.com>
|
||||
Date: Wed, 23 Aug 2023 09:26:36 +0200
|
||||
Subject: [XEN PATCH 01/10] x86/AMD: extend Zenbleed check to models "good"
|
||||
ucode isn't known for
|
||||
|
||||
Reportedly the AMD Custom APU 0405 found on SteamDeck, models 0x90 and
|
||||
0x91, (quoting the respective Linux commit) is similarly affected. Put
|
||||
another instance of our Zen1 vs Zen2 distinction checks in
|
||||
amd_check_zenbleed(), forcing use of the chickenbit irrespective of
|
||||
ucode version (building upon real hardware never surfacing a version of
|
||||
0xffffffff).
|
||||
|
||||
Signed-off-by: Jan Beulich <jbeulich@suse.com>
|
||||
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
(cherry picked from commit 145a69c0944ac70cfcf9d247c85dee9e99d9d302)
|
||||
---
|
||||
xen/arch/x86/cpu/amd.c | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c
|
||||
index 3ea214fc2e..1bb3044be1 100644
|
||||
--- a/xen/arch/x86/cpu/amd.c
|
||||
+++ b/xen/arch/x86/cpu/amd.c
|
||||
@@ -909,10 +909,17 @@ void amd_check_zenbleed(void)
|
||||
case 0xa0 ... 0xaf: good_rev = 0x08a00008; break;
|
||||
default:
|
||||
/*
|
||||
- * With the Fam17h check above, parts getting here are Zen1.
|
||||
- * They're not affected.
|
||||
+ * With the Fam17h check above, most parts getting here are
|
||||
+ * Zen1. They're not affected. Assume Zen2 ones making it
|
||||
+ * here are affected regardless of microcode version.
|
||||
+ *
|
||||
+ * Zen1 vs Zen2 isn't a simple model number comparison, so use
|
||||
+ * STIBP as a heuristic to distinguish.
|
||||
*/
|
||||
- return;
|
||||
+ if (!boot_cpu_has(X86_FEATURE_AMD_STIBP))
|
||||
+ return;
|
||||
+ good_rev = ~0U;
|
||||
+ break;
|
||||
}
|
||||
|
||||
rdmsrl(MSR_AMD64_DE_CFG, val);
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
From dc28aba565f226f9bec24cfde993e78478acfb4e Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <dc28aba565f226f9bec24cfde993e78478acfb4e.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Tue, 12 Sep 2023 15:06:49 +0100
|
||||
Subject: [XEN PATCH 02/10] x86/spec-ctrl: Fix confusion between
|
||||
SPEC_CTRL_EXIT_TO_XEN{,_IST}
|
||||
|
||||
c/s 3fffaf9c13e9 ("x86/entry: Avoid using alternatives in NMI/#MC paths")
|
||||
dropped the only user, leaving behind the (incorrect) implication that Xen had
|
||||
split exit paths.
|
||||
|
||||
Delete the unused SPEC_CTRL_EXIT_TO_XEN and rename SPEC_CTRL_EXIT_TO_XEN_IST
|
||||
to SPEC_CTRL_EXIT_TO_XEN for consistency.
|
||||
|
||||
No functional change.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 1c18d73774533a55ba9d1cbee8bdace03efdb5e7)
|
||||
---
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 10 ++--------
|
||||
xen/arch/x86/x86_64/entry.S | 2 +-
|
||||
2 files changed, 3 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index f23bb105c5..e8fd01243c 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -79,7 +79,6 @@
|
||||
* - SPEC_CTRL_ENTRY_FROM_PV
|
||||
* - SPEC_CTRL_ENTRY_FROM_INTR
|
||||
* - SPEC_CTRL_ENTRY_FROM_INTR_IST
|
||||
- * - SPEC_CTRL_EXIT_TO_XEN_IST
|
||||
* - SPEC_CTRL_EXIT_TO_XEN
|
||||
* - SPEC_CTRL_EXIT_TO_PV
|
||||
*
|
||||
@@ -268,11 +267,6 @@
|
||||
ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \
|
||||
X86_FEATURE_SC_MSR_PV
|
||||
|
||||
-/* Use when exiting to Xen context. */
|
||||
-#define SPEC_CTRL_EXIT_TO_XEN \
|
||||
- ALTERNATIVE "", \
|
||||
- DO_SPEC_CTRL_EXIT_TO_XEN, X86_FEATURE_SC_MSR_PV
|
||||
-
|
||||
/* Use when exiting to PV guest context. */
|
||||
#define SPEC_CTRL_EXIT_TO_PV \
|
||||
ALTERNATIVE "", \
|
||||
@@ -339,8 +333,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
UNLIKELY_END(\@_serialise)
|
||||
.endm
|
||||
|
||||
-/* Use when exiting to Xen in IST context. */
|
||||
-.macro SPEC_CTRL_EXIT_TO_XEN_IST
|
||||
+/* Use when exiting to Xen context. */
|
||||
+.macro SPEC_CTRL_EXIT_TO_XEN
|
||||
/*
|
||||
* Requires %rbx=stack_end
|
||||
* Clobbers %rax, %rcx, %rdx
|
||||
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
|
||||
index 7675a59ff0..b45a09823a 100644
|
||||
--- a/xen/arch/x86/x86_64/entry.S
|
||||
+++ b/xen/arch/x86/x86_64/entry.S
|
||||
@@ -673,7 +673,7 @@ UNLIKELY_START(ne, exit_cr3)
|
||||
UNLIKELY_END(exit_cr3)
|
||||
|
||||
/* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */
|
||||
- SPEC_CTRL_EXIT_TO_XEN_IST /* Req: %rbx=end, Clob: acd */
|
||||
+ SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */
|
||||
|
||||
RESTORE_ALL adj=8
|
||||
iretq
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
From 84690fb82c4f4aecb72a6789d8994efa74841e09 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <84690fb82c4f4aecb72a6789d8994efa74841e09.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Tue, 12 Sep 2023 17:03:16 +0100
|
||||
Subject: [XEN PATCH 03/10] x86/spec-ctrl: Fold DO_SPEC_CTRL_EXIT_TO_XEN into
|
||||
it's single user
|
||||
|
||||
With the SPEC_CTRL_EXIT_TO_XEN{,_IST} confusion fixed, it's now obvious that
|
||||
there's only a single EXIT_TO_XEN path. Fold DO_SPEC_CTRL_EXIT_TO_XEN into
|
||||
SPEC_CTRL_EXIT_TO_XEN to simplify further fixes.
|
||||
|
||||
When merging labels, switch the name to .L\@_skip_sc_msr as "skip" on its own
|
||||
is going to be too generic shortly.
|
||||
|
||||
No functional change.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 694bb0f280fd08a4377e36e32b84b5062def4de2)
|
||||
---
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 40 ++++++++++--------------
|
||||
1 file changed, 16 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index e8fd01243c..d5f65d80ea 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -211,27 +211,6 @@
|
||||
wrmsr
|
||||
.endm
|
||||
|
||||
-.macro DO_SPEC_CTRL_EXIT_TO_XEN
|
||||
-/*
|
||||
- * Requires %rbx=stack_end
|
||||
- * Clobbers %rax, %rcx, %rdx
|
||||
- *
|
||||
- * When returning to Xen context, look to see whether SPEC_CTRL shadowing is
|
||||
- * in effect, and reload the shadow value. This covers race conditions which
|
||||
- * exist with an NMI/MCE/etc hitting late in the return-to-guest path.
|
||||
- */
|
||||
- xor %edx, %edx
|
||||
-
|
||||
- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx)
|
||||
- jz .L\@_skip
|
||||
-
|
||||
- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax
|
||||
- mov $MSR_SPEC_CTRL, %ecx
|
||||
- wrmsr
|
||||
-
|
||||
-.L\@_skip:
|
||||
-.endm
|
||||
-
|
||||
.macro DO_SPEC_CTRL_EXIT_TO_GUEST
|
||||
/*
|
||||
* Requires %eax=spec_ctrl, %rsp=regs/cpuinfo
|
||||
@@ -340,11 +319,24 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
* Clobbers %rax, %rcx, %rdx
|
||||
*/
|
||||
testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx)
|
||||
- jz .L\@_skip
|
||||
+ jz .L\@_skip_sc_msr
|
||||
|
||||
- DO_SPEC_CTRL_EXIT_TO_XEN
|
||||
+ /*
|
||||
+ * When returning to Xen context, look to see whether SPEC_CTRL shadowing
|
||||
+ * is in effect, and reload the shadow value. This covers race conditions
|
||||
+ * which exist with an NMI/MCE/etc hitting late in the return-to-guest
|
||||
+ * path.
|
||||
+ */
|
||||
+ xor %edx, %edx
|
||||
|
||||
-.L\@_skip:
|
||||
+ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx)
|
||||
+ jz .L\@_skip_sc_msr
|
||||
+
|
||||
+ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax
|
||||
+ mov $MSR_SPEC_CTRL, %ecx
|
||||
+ wrmsr
|
||||
+
|
||||
+.L\@_skip_sc_msr:
|
||||
.endm
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
From 3952c73bdbd05f0e666986fce633a591237b3c88 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <3952c73bdbd05f0e666986fce633a591237b3c88.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Fri, 1 Sep 2023 11:38:44 +0100
|
||||
Subject: [XEN PATCH 04/10] x86/spec-ctrl: Turn the remaining
|
||||
SPEC_CTRL_{ENTRY,EXIT}_* into asm macros
|
||||
|
||||
These have grown more complex over time, with some already having been
|
||||
converted.
|
||||
|
||||
Provide full Requires/Clobbers comments, otherwise missing at this level of
|
||||
indirection.
|
||||
|
||||
No functional change.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 7125429aafb9e3c9c88fc93001fc2300e0ac2cc8)
|
||||
---
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 37 ++++++++++++++++++------
|
||||
1 file changed, 28 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index d5f65d80ea..c6d5f2ad01 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -231,26 +231,45 @@
|
||||
.endm
|
||||
|
||||
/* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */
|
||||
-#define SPEC_CTRL_ENTRY_FROM_PV \
|
||||
+.macro SPEC_CTRL_ENTRY_FROM_PV
|
||||
+/*
|
||||
+ * Requires %rsp=regs/cpuinfo, %rdx=0
|
||||
+ * Clobbers %rax, %rcx, %rdx
|
||||
+ */
|
||||
ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=0), \
|
||||
- X86_FEATURE_IBPB_ENTRY_PV; \
|
||||
- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \
|
||||
+ X86_FEATURE_IBPB_ENTRY_PV
|
||||
+
|
||||
+ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV
|
||||
+
|
||||
ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=0), \
|
||||
X86_FEATURE_SC_MSR_PV
|
||||
+.endm
|
||||
|
||||
/* Use in interrupt/exception context. May interrupt Xen or PV context. */
|
||||
-#define SPEC_CTRL_ENTRY_FROM_INTR \
|
||||
+.macro SPEC_CTRL_ENTRY_FROM_INTR
|
||||
+/*
|
||||
+ * Requires %rsp=regs, %r14=stack_end, %rdx=0
|
||||
+ * Clobbers %rax, %rcx, %rdx
|
||||
+ */
|
||||
ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=1), \
|
||||
- X86_FEATURE_IBPB_ENTRY_PV; \
|
||||
- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \
|
||||
+ X86_FEATURE_IBPB_ENTRY_PV
|
||||
+
|
||||
+ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV
|
||||
+
|
||||
ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \
|
||||
X86_FEATURE_SC_MSR_PV
|
||||
+.endm
|
||||
|
||||
/* Use when exiting to PV guest context. */
|
||||
-#define SPEC_CTRL_EXIT_TO_PV \
|
||||
- ALTERNATIVE "", \
|
||||
- DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \
|
||||
+.macro SPEC_CTRL_EXIT_TO_PV
|
||||
+/*
|
||||
+ * Requires %rax=spec_ctrl, %rsp=regs/info
|
||||
+ * Clobbers %rcx, %rdx
|
||||
+ */
|
||||
+ ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV
|
||||
+
|
||||
DO_SPEC_CTRL_COND_VERW
|
||||
+.endm
|
||||
|
||||
/*
|
||||
* Use in IST interrupt/exception context. May interrupt Xen or PV context.
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
From ba023e93d0b1e60b80251bf080bab694efb9f8e3 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <ba023e93d0b1e60b80251bf080bab694efb9f8e3.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Wed, 30 Aug 2023 20:11:50 +0100
|
||||
Subject: [XEN PATCH 05/10] x86/spec-ctrl: Improve all SPEC_CTRL_{ENTER,EXIT}_*
|
||||
comments
|
||||
|
||||
... to better explain how they're used.
|
||||
|
||||
Doing so highlights that SPEC_CTRL_EXIT_TO_XEN is missing a VERW flush for the
|
||||
corner case when e.g. an NMI hits late in an exit-to-guest path.
|
||||
|
||||
Leave a TODO, which will be addressed in subsequent patches which arrange for
|
||||
VERW flushing to be safe within SPEC_CTRL_EXIT_TO_XEN.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 45f00557350dc7d0756551069803fc49c29184ca)
|
||||
---
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 36 ++++++++++++++++++++----
|
||||
1 file changed, 31 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index c6d5f2ad01..97c4db31cd 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -230,7 +230,10 @@
|
||||
wrmsr
|
||||
.endm
|
||||
|
||||
-/* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */
|
||||
+/*
|
||||
+ * Used after an entry from PV context: SYSCALL, SYSENTER, INT,
|
||||
+ * etc. There is always a guest speculation state in context.
|
||||
+ */
|
||||
.macro SPEC_CTRL_ENTRY_FROM_PV
|
||||
/*
|
||||
* Requires %rsp=regs/cpuinfo, %rdx=0
|
||||
@@ -245,7 +248,11 @@
|
||||
X86_FEATURE_SC_MSR_PV
|
||||
.endm
|
||||
|
||||
-/* Use in interrupt/exception context. May interrupt Xen or PV context. */
|
||||
+/*
|
||||
+ * Used after an exception or maskable interrupt, hitting Xen or PV context.
|
||||
+ * There will either be a guest speculation context, or (barring fatal
|
||||
+ * exceptions) a well-formed Xen speculation context.
|
||||
+ */
|
||||
.macro SPEC_CTRL_ENTRY_FROM_INTR
|
||||
/*
|
||||
* Requires %rsp=regs, %r14=stack_end, %rdx=0
|
||||
@@ -260,7 +267,10 @@
|
||||
X86_FEATURE_SC_MSR_PV
|
||||
.endm
|
||||
|
||||
-/* Use when exiting to PV guest context. */
|
||||
+/*
|
||||
+ * Used when exiting from any entry context, back to PV context. This
|
||||
+ * includes from an IST entry which moved onto the primary stack.
|
||||
+ */
|
||||
.macro SPEC_CTRL_EXIT_TO_PV
|
||||
/*
|
||||
* Requires %rax=spec_ctrl, %rsp=regs/info
|
||||
@@ -272,7 +282,13 @@
|
||||
.endm
|
||||
|
||||
/*
|
||||
- * Use in IST interrupt/exception context. May interrupt Xen or PV context.
|
||||
+ * Used after an IST entry hitting Xen or PV context. Special care is needed,
|
||||
+ * because when hitting Xen context, there may not be a well-formed
|
||||
+ * speculation context. (i.e. it can hit in the middle of
|
||||
+ * SPEC_CTRL_{ENTRY,EXIT}_* regions.)
|
||||
+ *
|
||||
+ * An IST entry which hits PV context moves onto the primary stack and leaves
|
||||
+ * via SPEC_CTRL_EXIT_TO_PV, *not* SPEC_CTRL_EXIT_TO_XEN.
|
||||
*/
|
||||
.macro SPEC_CTRL_ENTRY_FROM_INTR_IST
|
||||
/*
|
||||
@@ -331,7 +347,14 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
UNLIKELY_END(\@_serialise)
|
||||
.endm
|
||||
|
||||
-/* Use when exiting to Xen context. */
|
||||
+/*
|
||||
+ * Use when exiting from any entry context, back to Xen context. This
|
||||
+ * includes returning to other SPEC_CTRL_{ENTRY,EXIT}_* regions with an
|
||||
+ * incomplete speculation context.
|
||||
+ *
|
||||
+ * Because we might have interrupted Xen beyond SPEC_CTRL_EXIT_TO_$GUEST, we
|
||||
+ * need to treat this as if it were an EXIT_TO_$GUEST case too.
|
||||
+ */
|
||||
.macro SPEC_CTRL_EXIT_TO_XEN
|
||||
/*
|
||||
* Requires %rbx=stack_end
|
||||
@@ -356,6 +379,9 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
wrmsr
|
||||
|
||||
.L\@_skip_sc_msr:
|
||||
+
|
||||
+ /* TODO VERW */
|
||||
+
|
||||
.endm
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
From 5f7efd47c8273fde972637d0360851802f76eca9 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <5f7efd47c8273fde972637d0360851802f76eca9.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Wed, 13 Sep 2023 13:48:16 +0100
|
||||
Subject: [XEN PATCH 06/10] x86/entry: Adjust restore_all_xen to hold stack_end
|
||||
in %r14
|
||||
|
||||
All other SPEC_CTRL_{ENTRY,EXIT}_* helpers hold stack_end in %r14. Adjust it
|
||||
for consistency.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 7aa28849a1155d856e214e9a80a7e65fffdc3e58)
|
||||
---
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 8 ++++----
|
||||
xen/arch/x86/x86_64/entry.S | 8 ++++----
|
||||
2 files changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index 97c4db31cd..66c706496f 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -357,10 +357,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
*/
|
||||
.macro SPEC_CTRL_EXIT_TO_XEN
|
||||
/*
|
||||
- * Requires %rbx=stack_end
|
||||
+ * Requires %r14=stack_end
|
||||
* Clobbers %rax, %rcx, %rdx
|
||||
*/
|
||||
- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx)
|
||||
+ testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14)
|
||||
jz .L\@_skip_sc_msr
|
||||
|
||||
/*
|
||||
@@ -371,10 +371,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
*/
|
||||
xor %edx, %edx
|
||||
|
||||
- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx)
|
||||
+ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14)
|
||||
jz .L\@_skip_sc_msr
|
||||
|
||||
- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax
|
||||
+ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax
|
||||
mov $MSR_SPEC_CTRL, %ecx
|
||||
wrmsr
|
||||
|
||||
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
|
||||
index b45a09823a..92279a225d 100644
|
||||
--- a/xen/arch/x86/x86_64/entry.S
|
||||
+++ b/xen/arch/x86/x86_64/entry.S
|
||||
@@ -665,15 +665,15 @@ restore_all_xen:
|
||||
* Check whether we need to switch to the per-CPU page tables, in
|
||||
* case we return to late PV exit code (from an NMI or #MC).
|
||||
*/
|
||||
- GET_STACK_END(bx)
|
||||
- cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%rbx)
|
||||
+ GET_STACK_END(14)
|
||||
+ cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14)
|
||||
UNLIKELY_START(ne, exit_cr3)
|
||||
- mov STACK_CPUINFO_FIELD(pv_cr3)(%rbx), %rax
|
||||
+ mov STACK_CPUINFO_FIELD(pv_cr3)(%r14), %rax
|
||||
mov %rax, %cr3
|
||||
UNLIKELY_END(exit_cr3)
|
||||
|
||||
/* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */
|
||||
- SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */
|
||||
+ SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */
|
||||
|
||||
RESTORE_ALL adj=8
|
||||
iretq
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
From e4a71bc0da0baf7464bb0d8e33053f330e5ea366 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <e4a71bc0da0baf7464bb0d8e33053f330e5ea366.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Wed, 13 Sep 2023 12:20:12 +0100
|
||||
Subject: [XEN PATCH 07/10] x86/entry: Track the IST-ness of an entry for the
|
||||
exit paths
|
||||
|
||||
Use %r12 to hold an ist_exit boolean. This register is zero elsewhere in the
|
||||
entry/exit asm, so it only needs setting in the IST path.
|
||||
|
||||
As this is subtle and fragile, add check_ist_exit() to be used in debugging
|
||||
builds to cross-check that the ist_exit boolean matches the entry vector.
|
||||
|
||||
Write check_ist_exit() it in C, because it's debug only and the logic more
|
||||
complicated than I care to maintain in asm.
|
||||
|
||||
For now, we only need to use this signal in the exit-to-Xen path, but some
|
||||
exit-to-guest paths happen in IST context too. Check the correctness in all
|
||||
exit paths to avoid the logic bit-rotting.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 21bdc25b05a0f8ab6bc73520a9ca01327360732c)
|
||||
|
||||
x86/entry: Partially revert IST-exit checks
|
||||
|
||||
The patch adding check_ist_exit() didn't account for the fact that
|
||||
reset_stack_and_jump() is not an ABI-preserving boundary. The IST-ness in
|
||||
%r12 doesn't survive into the next context, and is a stale value C.
|
||||
|
||||
This shows up in Gitlab CI for the Clang build:
|
||||
|
||||
https://gitlab.com/xen-project/people/andyhhp/xen/-/jobs/5112783827
|
||||
|
||||
and in OSSTest for GCC 8:
|
||||
|
||||
http://logs.test-lab.xenproject.org/osstest/logs/183045/test-amd64-amd64-xl-qemuu-debianhvm-amd64/serial-pinot0.log
|
||||
|
||||
There's no straightforward way to reconstruct the IST-exit-ness on the
|
||||
exit-to-guest path after a context switch. For now, we only need IST-exit on
|
||||
the return-to-Xen path.
|
||||
|
||||
Fixes: 21bdc25b05a0 ("x86/entry: Track the IST-ness of an entry for the exit paths")
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 9b57c800b79b96769ea3dcd6468578fa664d19f9)
|
||||
---
|
||||
xen/arch/x86/traps.c | 13 +++++++++++++
|
||||
xen/arch/x86/x86_64/entry.S | 13 ++++++++++++-
|
||||
2 files changed, 25 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
|
||||
index d12004b1c6..e65cc60041 100644
|
||||
--- a/xen/arch/x86/traps.c
|
||||
+++ b/xen/arch/x86/traps.c
|
||||
@@ -2315,6 +2315,19 @@ void asm_domain_crash_synchronous(unsigned long addr)
|
||||
do_softirq();
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_DEBUG
|
||||
+void check_ist_exit(const struct cpu_user_regs *regs, bool ist_exit)
|
||||
+{
|
||||
+ const unsigned int ist_mask =
|
||||
+ (1U << X86_EXC_NMI) | (1U << X86_EXC_DB) |
|
||||
+ (1U << X86_EXC_DF) | (1U << X86_EXC_MC);
|
||||
+ uint8_t ev = regs->entry_vector;
|
||||
+ bool is_ist = (ev < TRAP_nr) && ((1U << ev) & ist_mask);
|
||||
+
|
||||
+ ASSERT(is_ist == ist_exit);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
|
||||
index 92279a225d..4cebc4fbe3 100644
|
||||
--- a/xen/arch/x86/x86_64/entry.S
|
||||
+++ b/xen/arch/x86/x86_64/entry.S
|
||||
@@ -659,8 +659,15 @@ ENTRY(early_page_fault)
|
||||
.section .text.entry, "ax", @progbits
|
||||
|
||||
ALIGN
|
||||
-/* No special register assumptions. */
|
||||
+/* %r12=ist_exit */
|
||||
restore_all_xen:
|
||||
+
|
||||
+#ifdef CONFIG_DEBUG
|
||||
+ mov %rsp, %rdi
|
||||
+ mov %r12, %rsi
|
||||
+ call check_ist_exit
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Check whether we need to switch to the per-CPU page tables, in
|
||||
* case we return to late PV exit code (from an NMI or #MC).
|
||||
@@ -1091,6 +1098,10 @@ handle_ist_exception:
|
||||
.L_ist_dispatch_done:
|
||||
mov %r15, STACK_CPUINFO_FIELD(xen_cr3)(%r14)
|
||||
mov %bl, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14)
|
||||
+
|
||||
+ /* This is an IST exit */
|
||||
+ mov $1, %r12d
|
||||
+
|
||||
cmpb $TRAP_nmi,UREGS_entry_vector(%rsp)
|
||||
jne ret_from_intr
|
||||
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
From 2e2c3efcfc9f183674a8de6ed954ffbe7188b70d Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <2e2c3efcfc9f183674a8de6ed954ffbe7188b70d.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Wed, 13 Sep 2023 13:53:33 +0100
|
||||
Subject: [XEN PATCH 08/10] x86/spec-ctrl: Issue VERW during IST exit to Xen
|
||||
|
||||
There is a corner case where e.g. an NMI hitting an exit-to-guest path after
|
||||
SPEC_CTRL_EXIT_TO_* would have run the entire NMI handler *after* the VERW
|
||||
flush to scrub potentially sensitive data from uarch buffers.
|
||||
|
||||
In order to compensate, issue VERW when exiting to Xen from an IST entry.
|
||||
|
||||
SPEC_CTRL_EXIT_TO_XEN already has two reads of spec_ctrl_flags off the stack,
|
||||
and we're about to add a third. Load the field into %ebx, and list the
|
||||
register as clobbered.
|
||||
|
||||
%r12 has been arranged to be the ist_exit signal, so add this as an input
|
||||
dependency and use it to identify when to issue a VERW.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit 3ee6066bcd737756b0990d417d94eddc0b0d2585)
|
||||
---
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 20 +++++++++++++++-----
|
||||
xen/arch/x86/x86_64/entry.S | 2 +-
|
||||
2 files changed, 16 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index 66c706496f..28a75796e6 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -357,10 +357,12 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
*/
|
||||
.macro SPEC_CTRL_EXIT_TO_XEN
|
||||
/*
|
||||
- * Requires %r14=stack_end
|
||||
- * Clobbers %rax, %rcx, %rdx
|
||||
+ * Requires %r12=ist_exit, %r14=stack_end
|
||||
+ * Clobbers %rax, %rbx, %rcx, %rdx
|
||||
*/
|
||||
- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14)
|
||||
+ movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx
|
||||
+
|
||||
+ testb $SCF_ist_sc_msr, %bl
|
||||
jz .L\@_skip_sc_msr
|
||||
|
||||
/*
|
||||
@@ -371,7 +373,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
*/
|
||||
xor %edx, %edx
|
||||
|
||||
- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14)
|
||||
+ testb $SCF_use_shadow, %bl
|
||||
jz .L\@_skip_sc_msr
|
||||
|
||||
mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax
|
||||
@@ -380,8 +382,16 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
|
||||
.L\@_skip_sc_msr:
|
||||
|
||||
- /* TODO VERW */
|
||||
+ test %r12, %r12
|
||||
+ jz .L\@_skip_ist_exit
|
||||
+
|
||||
+ /* Logically DO_SPEC_CTRL_COND_VERW but without the %rsp=cpuinfo dependency */
|
||||
+ testb $SCF_verw, %bl
|
||||
+ jz .L\@_skip_verw
|
||||
+ verw STACK_CPUINFO_FIELD(verw_sel)(%r14)
|
||||
+.L\@_skip_verw:
|
||||
|
||||
+.L\@_skip_ist_exit:
|
||||
.endm
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
|
||||
index 4cebc4fbe3..c12e011b4d 100644
|
||||
--- a/xen/arch/x86/x86_64/entry.S
|
||||
+++ b/xen/arch/x86/x86_64/entry.S
|
||||
@@ -680,7 +680,7 @@ UNLIKELY_START(ne, exit_cr3)
|
||||
UNLIKELY_END(exit_cr3)
|
||||
|
||||
/* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */
|
||||
- SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */
|
||||
+ SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end, Clob: abcd */
|
||||
|
||||
RESTORE_ALL adj=8
|
||||
iretq
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
From 19ee1e1faa32b79274b3484cb1170a5970f1e602 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <19ee1e1faa32b79274b3484cb1170a5970f1e602.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Fri, 15 Sep 2023 12:13:51 +0100
|
||||
Subject: [XEN PATCH 09/10] x86/amd: Introduce is_zen{1,2}_uarch() predicates
|
||||
|
||||
We already have 3 cases using STIBP as a Zen1/2 heuristic, and are about to
|
||||
introduce a 4th. Wrap the heuristic into a pair of predicates rather than
|
||||
opencoding it, and the explanation of the heuristic, at each usage site.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit de1d265001397f308c5c3c5d3ffc30e7ef8c0705)
|
||||
---
|
||||
xen/arch/x86/cpu/amd.c | 18 ++++--------------
|
||||
xen/arch/x86/include/asm/amd.h | 11 +++++++++++
|
||||
2 files changed, 15 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c
|
||||
index 1bb3044be1..e94ba5a0e0 100644
|
||||
--- a/xen/arch/x86/cpu/amd.c
|
||||
+++ b/xen/arch/x86/cpu/amd.c
|
||||
@@ -855,15 +855,13 @@ void amd_set_legacy_ssbd(bool enable)
|
||||
* non-branch instructions to be ignored. It is to be set unilaterally in
|
||||
* newer microcode.
|
||||
*
|
||||
- * This chickenbit is something unrelated on Zen1, and Zen1 vs Zen2 isn't a
|
||||
- * simple model number comparison, so use STIBP as a heuristic to separate the
|
||||
- * two uarches in Fam17h(AMD)/18h(Hygon).
|
||||
+ * This chickenbit is something unrelated on Zen1.
|
||||
*/
|
||||
void amd_init_spectral_chicken(void)
|
||||
{
|
||||
uint64_t val, chickenbit = 1 << 1;
|
||||
|
||||
- if (cpu_has_hypervisor || !boot_cpu_has(X86_FEATURE_AMD_STIBP))
|
||||
+ if (cpu_has_hypervisor || !is_zen2_uarch())
|
||||
return;
|
||||
|
||||
if (rdmsr_safe(MSR_AMD64_DE_CFG2, val) == 0 && !(val & chickenbit))
|
||||
@@ -912,11 +910,8 @@ void amd_check_zenbleed(void)
|
||||
* With the Fam17h check above, most parts getting here are
|
||||
* Zen1. They're not affected. Assume Zen2 ones making it
|
||||
* here are affected regardless of microcode version.
|
||||
- *
|
||||
- * Zen1 vs Zen2 isn't a simple model number comparison, so use
|
||||
- * STIBP as a heuristic to distinguish.
|
||||
*/
|
||||
- if (!boot_cpu_has(X86_FEATURE_AMD_STIBP))
|
||||
+ if (is_zen1_uarch())
|
||||
return;
|
||||
good_rev = ~0U;
|
||||
break;
|
||||
@@ -1277,12 +1272,7 @@ static int __init cf_check zen2_c6_errata_check(void)
|
||||
*/
|
||||
s_time_t delta;
|
||||
|
||||
- /*
|
||||
- * Zen1 vs Zen2 isn't a simple model number comparison, so use STIBP as
|
||||
- * a heuristic to separate the two uarches in Fam17h.
|
||||
- */
|
||||
- if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 ||
|
||||
- !boot_cpu_has(X86_FEATURE_AMD_STIBP))
|
||||
+ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || !is_zen2_uarch())
|
||||
return 0;
|
||||
|
||||
/*
|
||||
diff --git a/xen/arch/x86/include/asm/amd.h b/xen/arch/x86/include/asm/amd.h
|
||||
index a975d3de26..82324110ab 100644
|
||||
--- a/xen/arch/x86/include/asm/amd.h
|
||||
+++ b/xen/arch/x86/include/asm/amd.h
|
||||
@@ -140,6 +140,17 @@
|
||||
AMD_MODEL_RANGE(0x11, 0x0, 0x0, 0xff, 0xf), \
|
||||
AMD_MODEL_RANGE(0x12, 0x0, 0x0, 0xff, 0xf))
|
||||
|
||||
+/*
|
||||
+ * The Zen1 and Zen2 microarchitectures are implemented by AMD (Fam17h) and
|
||||
+ * Hygon (Fam18h) but without simple model number rules. Instead, use STIBP
|
||||
+ * as a heuristic that distinguishes the two.
|
||||
+ *
|
||||
+ * The caller is required to perform the appropriate vendor/family checks
|
||||
+ * first.
|
||||
+ */
|
||||
+#define is_zen1_uarch() (!boot_cpu_has(X86_FEATURE_AMD_STIBP))
|
||||
+#define is_zen2_uarch() boot_cpu_has(X86_FEATURE_AMD_STIBP)
|
||||
+
|
||||
struct cpuinfo_x86;
|
||||
int cpu_has_amd_erratum(const struct cpuinfo_x86 *, int, ...);
|
||||
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,231 +0,0 @@
|
|||
From 9ac2f49f5fa3a5159409241d4f74fb0d721dd4c5 Mon Sep 17 00:00:00 2001
|
||||
Message-ID: <9ac2f49f5fa3a5159409241d4f74fb0d721dd4c5.1695733540.git.m.a.young@durham.ac.uk>
|
||||
In-Reply-To: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
References: <d2d2dcae879c6cc05227c9620f0a772f35fe6886.1695733540.git.m.a.young@durham.ac.uk>
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Wed, 30 Aug 2023 20:24:25 +0100
|
||||
Subject: [XEN PATCH 10/10] x86/spec-ctrl: Mitigate the Zen1 DIV leakage
|
||||
|
||||
In the Zen1 microarchitecure, there is one divider in the pipeline which
|
||||
services uops from both threads. In the case of #DE, the latched result from
|
||||
the previous DIV to execute will be forwarded speculatively.
|
||||
|
||||
This is an interesting covert channel that allows two threads to communicate
|
||||
without any system calls. In also allows userspace to obtain the result of
|
||||
the most recent DIV instruction executed (even speculatively) in the core,
|
||||
which can be from a higher privilege context.
|
||||
|
||||
Scrub the result from the divider by executing a non-faulting divide. This
|
||||
needs performing on the exit-to-guest paths, and ist_exit-to-Xen.
|
||||
|
||||
Alternatives in IST context is believed safe now that it's done in NMI
|
||||
context.
|
||||
|
||||
This is XSA-439 / CVE-2023-20588.
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
(cherry picked from commit b5926c6ecf05c28ee99c6248c42d691ccbf0c315)
|
||||
---
|
||||
docs/misc/xen-command-line.pandoc | 6 ++-
|
||||
xen/arch/x86/hvm/svm/entry.S | 1 +
|
||||
xen/arch/x86/include/asm/cpufeatures.h | 2 +-
|
||||
xen/arch/x86/include/asm/spec_ctrl_asm.h | 17 +++++++++
|
||||
xen/arch/x86/spec_ctrl.c | 48 +++++++++++++++++++++++-
|
||||
5 files changed, 71 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
|
||||
index d9dae740cc..b92c8f969c 100644
|
||||
--- a/docs/misc/xen-command-line.pandoc
|
||||
+++ b/docs/misc/xen-command-line.pandoc
|
||||
@@ -2315,7 +2315,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`).
|
||||
> {msr-sc,rsb,md-clear,ibpb-entry}=<bool>|{pv,hvm}=<bool>,
|
||||
> bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd,
|
||||
> eager-fpu,l1d-flush,branch-harden,srb-lock,
|
||||
-> unpriv-mmio,gds-mit}=<bool> ]`
|
||||
+> unpriv-mmio,gds-mit,div-scrub}=<bool> ]`
|
||||
|
||||
Controls for speculative execution sidechannel mitigations. By default, Xen
|
||||
will pick the most appropriate mitigations based on compiled in support,
|
||||
@@ -2437,6 +2437,10 @@ has elected not to lock the configuration, Xen will use GDS_CTRL to mitigate
|
||||
GDS with. Otherwise, Xen will mitigate by disabling AVX, which blocks the use
|
||||
of the AVX2 Gather instructions.
|
||||
|
||||
+On all hardware, the `div-scrub=` option can be used to force or prevent Xen
|
||||
+from mitigating the DIV-leakage vulnerability. By default, Xen will mitigate
|
||||
+DIV-leakage on hardware believed to be vulnerable.
|
||||
+
|
||||
### sync_console
|
||||
> `= <boolean>`
|
||||
|
||||
diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S
|
||||
index 981cd82e7c..934f12cf5c 100644
|
||||
--- a/xen/arch/x86/hvm/svm/entry.S
|
||||
+++ b/xen/arch/x86/hvm/svm/entry.S
|
||||
@@ -74,6 +74,7 @@ __UNLIKELY_END(nsvm_hap)
|
||||
1: /* No Spectre v1 concerns. Execution will hit VMRUN imminently. */
|
||||
.endm
|
||||
ALTERNATIVE "", svm_vmentry_spec_ctrl, X86_FEATURE_SC_MSR_HVM
|
||||
+ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV
|
||||
|
||||
pop %r15
|
||||
pop %r14
|
||||
diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h
|
||||
index da0593de85..c3aad21c3b 100644
|
||||
--- a/xen/arch/x86/include/asm/cpufeatures.h
|
||||
+++ b/xen/arch/x86/include/asm/cpufeatures.h
|
||||
@@ -35,7 +35,7 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM
|
||||
XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */
|
||||
XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */
|
||||
XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */
|
||||
-/* Bits 23 unused. */
|
||||
+XEN_CPUFEATURE(SC_DIV, X86_SYNTH(23)) /* DIV scrub needed */
|
||||
XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */
|
||||
XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */
|
||||
XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */
|
||||
diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
index 28a75796e6..f4b8b9d956 100644
|
||||
--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h
|
||||
@@ -177,6 +177,19 @@
|
||||
.L\@_verw_skip:
|
||||
.endm
|
||||
|
||||
+.macro DO_SPEC_CTRL_DIV
|
||||
+/*
|
||||
+ * Requires nothing
|
||||
+ * Clobbers %rax
|
||||
+ *
|
||||
+ * Issue a DIV for its flushing side effect (Zen1 uarch specific). Any
|
||||
+ * non-faulting DIV will do; a byte DIV has least latency, and doesn't clobber
|
||||
+ * %rdx.
|
||||
+ */
|
||||
+ mov $1, %eax
|
||||
+ div %al
|
||||
+.endm
|
||||
+
|
||||
.macro DO_SPEC_CTRL_ENTRY maybexen:req
|
||||
/*
|
||||
* Requires %rsp=regs (also cpuinfo if !maybexen)
|
||||
@@ -279,6 +292,8 @@
|
||||
ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV
|
||||
|
||||
DO_SPEC_CTRL_COND_VERW
|
||||
+
|
||||
+ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV
|
||||
.endm
|
||||
|
||||
/*
|
||||
@@ -391,6 +406,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise):
|
||||
verw STACK_CPUINFO_FIELD(verw_sel)(%r14)
|
||||
.L\@_skip_verw:
|
||||
|
||||
+ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV
|
||||
+
|
||||
.L\@_skip_ist_exit:
|
||||
.endm
|
||||
|
||||
diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c
|
||||
index 79b98f0fe7..0ff3c895ac 100644
|
||||
--- a/xen/arch/x86/spec_ctrl.c
|
||||
+++ b/xen/arch/x86/spec_ctrl.c
|
||||
@@ -79,6 +79,7 @@ static int8_t __initdata opt_srb_lock = -1;
|
||||
static bool __initdata opt_unpriv_mmio;
|
||||
static bool __ro_after_init opt_fb_clear_mmio;
|
||||
static int8_t __initdata opt_gds_mit = -1;
|
||||
+static int8_t __initdata opt_div_scrub = -1;
|
||||
|
||||
static int __init cf_check parse_spec_ctrl(const char *s)
|
||||
{
|
||||
@@ -133,6 +134,7 @@ static int __init cf_check parse_spec_ctrl(const char *s)
|
||||
opt_srb_lock = 0;
|
||||
opt_unpriv_mmio = false;
|
||||
opt_gds_mit = 0;
|
||||
+ opt_div_scrub = 0;
|
||||
}
|
||||
else if ( val > 0 )
|
||||
rc = -EINVAL;
|
||||
@@ -285,6 +287,8 @@ static int __init cf_check parse_spec_ctrl(const char *s)
|
||||
opt_unpriv_mmio = val;
|
||||
else if ( (val = parse_boolean("gds-mit", s, ss)) >= 0 )
|
||||
opt_gds_mit = val;
|
||||
+ else if ( (val = parse_boolean("div-scrub", s, ss)) >= 0 )
|
||||
+ opt_div_scrub = val;
|
||||
else
|
||||
rc = -EINVAL;
|
||||
|
||||
@@ -485,7 +489,7 @@ static void __init print_details(enum ind_thunk thunk)
|
||||
"\n");
|
||||
|
||||
/* Settings for Xen's protection, irrespective of guests. */
|
||||
- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s\n",
|
||||
+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n",
|
||||
thunk == THUNK_NONE ? "N/A" :
|
||||
thunk == THUNK_RETPOLINE ? "RETPOLINE" :
|
||||
thunk == THUNK_LFENCE ? "LFENCE" :
|
||||
@@ -510,6 +514,7 @@ static void __init print_details(enum ind_thunk thunk)
|
||||
opt_l1d_flush ? " L1D_FLUSH" : "",
|
||||
opt_md_clear_pv || opt_md_clear_hvm ||
|
||||
opt_fb_clear_mmio ? " VERW" : "",
|
||||
+ opt_div_scrub ? " DIV" : "",
|
||||
opt_branch_harden ? " BRANCH_HARDEN" : "");
|
||||
|
||||
/* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */
|
||||
@@ -967,6 +972,45 @@ static void __init srso_calculations(bool hw_smt_enabled)
|
||||
setup_force_cpu_cap(X86_FEATURE_SRSO_NO);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * The Div leakage issue is specific to the AMD Zen1 microarchitecure.
|
||||
+ *
|
||||
+ * However, there's no $FOO_NO bit defined, so if we're virtualised we have no
|
||||
+ * hope of spotting the case where we might move to vulnerable hardware. We
|
||||
+ * also can't make any useful conclusion about SMT-ness.
|
||||
+ *
|
||||
+ * Don't check the hypervisor bit, so at least we do the safe thing when
|
||||
+ * booting on something that looks like a Zen1 CPU.
|
||||
+ */
|
||||
+static bool __init has_div_vuln(void)
|
||||
+{
|
||||
+ if ( !(boot_cpu_data.x86_vendor &
|
||||
+ (X86_VENDOR_AMD | X86_VENDOR_HYGON)) )
|
||||
+ return false;
|
||||
+
|
||||
+ if ( boot_cpu_data.x86 != 0x17 && boot_cpu_data.x86 != 0x18 )
|
||||
+ return false;
|
||||
+
|
||||
+ return is_zen1_uarch();
|
||||
+}
|
||||
+
|
||||
+static void __init div_calculations(bool hw_smt_enabled)
|
||||
+{
|
||||
+ bool cpu_bug_div = has_div_vuln();
|
||||
+
|
||||
+ if ( opt_div_scrub == -1 )
|
||||
+ opt_div_scrub = cpu_bug_div;
|
||||
+
|
||||
+ if ( opt_div_scrub )
|
||||
+ setup_force_cpu_cap(X86_FEATURE_SC_DIV);
|
||||
+
|
||||
+ if ( opt_smt == -1 && !cpu_has_hypervisor && cpu_bug_div && hw_smt_enabled )
|
||||
+ warning_add(
|
||||
+ "Booted on leaky-DIV hardware with SMT/Hyperthreading\n"
|
||||
+ "enabled. Please assess your configuration and choose an\n"
|
||||
+ "explicit 'smt=<bool>' setting. See XSA-439.\n");
|
||||
+}
|
||||
+
|
||||
static void __init ibpb_calculations(void)
|
||||
{
|
||||
bool def_ibpb_entry = false;
|
||||
@@ -1726,6 +1770,8 @@ void __init init_speculation_mitigations(void)
|
||||
|
||||
ibpb_calculations();
|
||||
|
||||
+ div_calculations(hw_smt_enabled);
|
||||
+
|
||||
/* Check whether Eager FPU should be enabled by default. */
|
||||
if ( opt_eager_fpu == -1 )
|
||||
opt_eager_fpu = should_use_eager_fpu();
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
From 5d8b3d1ec98e56155d9650d7f4a70cd8ba9dc27d Mon Sep 17 00:00:00 2001
|
||||
From: Julien Grall <jgrall@amazon.com>
|
||||
Date: Fri, 22 Sep 2023 11:32:16 +0100
|
||||
Subject: tools/xenstored: domain_entry_fix(): Handle conflicting transaction
|
||||
|
||||
The function domain_entry_fix() will be initially called to check if the
|
||||
quota is correct before attempt to commit any nodes. So it would be
|
||||
possible that accounting is temporarily negative. This is the case
|
||||
in the following sequence:
|
||||
|
||||
1) Create 50 nodes
|
||||
2) Start two transactions
|
||||
3) Delete all the nodes in each transaction
|
||||
4) Commit the two transactions
|
||||
|
||||
Because the first transaction will have succeed and updated the
|
||||
accounting, there is no guarantee that 'd->nbentry + num' will still
|
||||
be above 0. So the assert() would be triggered.
|
||||
The assert() was introduced in dbef1f748289 ("tools/xenstore: simplify
|
||||
and fix per domain node accounting") with the assumption that the
|
||||
value can't be negative. As this is not true revert to the original
|
||||
check but restricted to the path where we don't update. Take the
|
||||
opportunity to explain the rationale behind the check.
|
||||
|
||||
This CVE-2023-34323 / XSA-440.
|
||||
|
||||
Reported-by: Stanislav Uschakow <suschako@amazon.de>
|
||||
Fixes: dbef1f748289 ("tools/xenstore: simplify and fix per domain node accounting")
|
||||
Signed-off-by: Julien Grall <jgrall@amazon.com>
|
||||
Reviewed-by: Juergen Gross <jgross@suse.com>
|
||||
|
||||
diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c
|
||||
index aa86892fed9e..6074df210c6e 100644
|
||||
--- a/tools/xenstore/xenstored_domain.c
|
||||
+++ b/tools/xenstore/xenstored_domain.c
|
||||
@@ -1094,10 +1094,20 @@ int domain_entry_fix(unsigned int domid, int num, bool update)
|
||||
}
|
||||
|
||||
cnt = d->nbentry + num;
|
||||
- assert(cnt >= 0);
|
||||
|
||||
- if (update)
|
||||
+ if (update) {
|
||||
+ assert(cnt >= 0);
|
||||
d->nbentry = cnt;
|
||||
+ } else if (cnt < 0) {
|
||||
+ /*
|
||||
+ * In a transaction when a node is being added/removed AND
|
||||
+ * the same node has been added/removed outside the
|
||||
+ * transaction in parallel, the result value may be negative.
|
||||
+ * This is no problem, as the transaction will fail due to
|
||||
+ * the resulting conflict. So override 'cnt'.
|
||||
+ */
|
||||
+ cnt = 0;
|
||||
+ }
|
||||
|
||||
return domid_is_unprivileged(domid) ? cnt : 0;
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
From 5b2ccb60ff22fbff44dd66214c2956a434ee6271 Mon Sep 17 00:00:00 2001
|
||||
From: Roger Pau Monne <roger.pau@citrix.com>
|
||||
Date: Tue, 13 Jun 2023 15:01:05 +0200
|
||||
Subject: [PATCH] iommu/amd-vi: flush IOMMU TLB when flushing the DTE
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The caching invalidation guidelines from the AMD-Vi specification (48882—Rev
|
||||
3.07-PUB—Oct 2022) seem to be misleading on some hardware, as devices will
|
||||
malfunction (see stale DMA mappings) if some fields of the DTE are updated but
|
||||
the IOMMU TLB is not flushed. This has been observed in practice on AMD
|
||||
systems. Due to the lack of guidance from the currently published
|
||||
specification this patch aims to increase the flushing done in order to prevent
|
||||
device malfunction.
|
||||
|
||||
In order to fix, issue an INVALIDATE_IOMMU_PAGES command from
|
||||
amd_iommu_flush_device(), flushing all the address space. Note this requires
|
||||
callers to be adjusted in order to pass the DomID on the DTE previous to the
|
||||
modification.
|
||||
|
||||
Some call sites don't provide a valid DomID to amd_iommu_flush_device() in
|
||||
order to avoid the flush. That's because the device had address translations
|
||||
disabled and hence the previous DomID on the DTE is not valid. Note the
|
||||
current logic relies on the entity disabling address translations to also flush
|
||||
the TLB of the in use DomID.
|
||||
|
||||
Device I/O TLB flushing when ATS are enabled is not covered by the current
|
||||
change, as ATS usage is not security supported.
|
||||
|
||||
This is XSA-442 / CVE-2023-34326
|
||||
|
||||
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
---
|
||||
xen/drivers/passthrough/amd/iommu.h | 3 ++-
|
||||
xen/drivers/passthrough/amd/iommu_cmd.c | 10 +++++++++-
|
||||
xen/drivers/passthrough/amd/iommu_guest.c | 5 +++--
|
||||
xen/drivers/passthrough/amd/iommu_init.c | 6 +++++-
|
||||
xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++++++++++----
|
||||
5 files changed, 29 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/xen/drivers/passthrough/amd/iommu.h b/xen/drivers/passthrough/amd/iommu.h
|
||||
index 5429ada58ef5..a58be28bf96d 100644
|
||||
--- a/xen/drivers/passthrough/amd/iommu.h
|
||||
+++ b/xen/drivers/passthrough/amd/iommu.h
|
||||
@@ -283,7 +283,8 @@ void amd_iommu_flush_pages(struct domain *d, unsigned long dfn,
|
||||
unsigned int order);
|
||||
void amd_iommu_flush_iotlb(u8 devfn, const struct pci_dev *pdev,
|
||||
uint64_t gaddr, unsigned int order);
|
||||
-void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf);
|
||||
+void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf,
|
||||
+ domid_t domid);
|
||||
void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf);
|
||||
void amd_iommu_flush_all_caches(struct amd_iommu *iommu);
|
||||
|
||||
diff --git a/xen/drivers/passthrough/amd/iommu_cmd.c b/xen/drivers/passthrough/amd/iommu_cmd.c
|
||||
index 40ddf366bb4d..cb28b36abc38 100644
|
||||
--- a/xen/drivers/passthrough/amd/iommu_cmd.c
|
||||
+++ b/xen/drivers/passthrough/amd/iommu_cmd.c
|
||||
@@ -363,10 +363,18 @@ void amd_iommu_flush_pages(struct domain *d,
|
||||
_amd_iommu_flush_pages(d, __dfn_to_daddr(dfn), order);
|
||||
}
|
||||
|
||||
-void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf)
|
||||
+void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf,
|
||||
+ domid_t domid)
|
||||
{
|
||||
invalidate_dev_table_entry(iommu, bdf);
|
||||
flush_command_buffer(iommu, 0);
|
||||
+
|
||||
+ /* Also invalidate IOMMU TLB entries when flushing the DTE. */
|
||||
+ if ( domid != DOMID_INVALID )
|
||||
+ {
|
||||
+ invalidate_iommu_pages(iommu, INV_IOMMU_ALL_PAGES_ADDRESS, domid, 0);
|
||||
+ flush_command_buffer(iommu, 0);
|
||||
+ }
|
||||
}
|
||||
|
||||
void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf)
|
||||
diff --git a/xen/drivers/passthrough/amd/iommu_guest.c b/xen/drivers/passthrough/amd/iommu_guest.c
|
||||
index 80a331f546ed..be86bce6fb03 100644
|
||||
--- a/xen/drivers/passthrough/amd/iommu_guest.c
|
||||
+++ b/xen/drivers/passthrough/amd/iommu_guest.c
|
||||
@@ -385,7 +385,7 @@ static int do_completion_wait(struct domain *d, cmd_entry_t *cmd)
|
||||
|
||||
static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd)
|
||||
{
|
||||
- uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id;
|
||||
+ uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id, prev_domid;
|
||||
struct amd_iommu_dte *gdte, *mdte, *dte_base;
|
||||
struct amd_iommu *iommu = NULL;
|
||||
struct guest_iommu *g_iommu;
|
||||
@@ -445,13 +445,14 @@ static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd)
|
||||
req_id = get_dma_requestor_id(iommu->seg, mbdf);
|
||||
dte_base = iommu->dev_table.buffer;
|
||||
mdte = &dte_base[req_id];
|
||||
+ prev_domid = mdte->domain_id;
|
||||
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
dte_set_gcr3_table(mdte, hdom_id, gcr3_mfn << PAGE_SHIFT, gv, glx);
|
||||
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
- amd_iommu_flush_device(iommu, req_id);
|
||||
+ amd_iommu_flush_device(iommu, req_id, prev_domid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/xen/drivers/passthrough/amd/iommu_init.c b/xen/drivers/passthrough/amd/iommu_init.c
|
||||
index 166570648d26..101a60ce1794 100644
|
||||
--- a/xen/drivers/passthrough/amd/iommu_init.c
|
||||
+++ b/xen/drivers/passthrough/amd/iommu_init.c
|
||||
@@ -1547,7 +1547,11 @@ static int cf_check _invalidate_all_devices(
|
||||
req_id = ivrs_mappings[bdf].dte_requestor_id;
|
||||
if ( iommu )
|
||||
{
|
||||
- amd_iommu_flush_device(iommu, req_id);
|
||||
+ /*
|
||||
+ * IOMMU TLB flush performed separately (see
|
||||
+ * invalidate_all_domain_pages()).
|
||||
+ */
|
||||
+ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID);
|
||||
amd_iommu_flush_intremap(iommu, req_id);
|
||||
}
|
||||
}
|
||||
diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c
|
||||
index 94e37755064b..8641b84712a0 100644
|
||||
--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c
|
||||
+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c
|
||||
@@ -192,10 +192,13 @@ static int __must_check amd_iommu_setup_domain_device(
|
||||
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
- amd_iommu_flush_device(iommu, req_id);
|
||||
+ /* DTE didn't have DMA translations enabled, do not flush the TLB. */
|
||||
+ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID);
|
||||
}
|
||||
else if ( dte->pt_root != mfn_x(page_to_mfn(root_pg)) )
|
||||
{
|
||||
+ domid_t prev_domid = dte->domain_id;
|
||||
+
|
||||
/*
|
||||
* Strictly speaking if the device is the only one with this requestor
|
||||
* ID, it could be allowed to be re-assigned regardless of unity map
|
||||
@@ -252,7 +255,7 @@ static int __must_check amd_iommu_setup_domain_device(
|
||||
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
- amd_iommu_flush_device(iommu, req_id);
|
||||
+ amd_iommu_flush_device(iommu, req_id, prev_domid);
|
||||
}
|
||||
else
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
@@ -421,6 +424,8 @@ static void amd_iommu_disable_domain_device(const struct domain *domain,
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
if ( dte->tv || dte->v )
|
||||
{
|
||||
+ domid_t prev_domid = dte->domain_id;
|
||||
+
|
||||
/* See the comment in amd_iommu_setup_device_table(). */
|
||||
dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_ABORTED;
|
||||
smp_wmb();
|
||||
@@ -439,7 +444,7 @@ static void amd_iommu_disable_domain_device(const struct domain *domain,
|
||||
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
- amd_iommu_flush_device(iommu, req_id);
|
||||
+ amd_iommu_flush_device(iommu, req_id, prev_domid);
|
||||
|
||||
AMD_IOMMU_DEBUG("Disable: device id = %#x, "
|
||||
"domain = %d, paging mode = %d\n",
|
||||
@@ -610,7 +615,8 @@ static int cf_check amd_iommu_add_device(u8 devfn, struct pci_dev *pdev)
|
||||
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
- amd_iommu_flush_device(iommu, bdf);
|
||||
+ /* DTE didn't have DMA translations enabled, do not flush the TLB. */
|
||||
+ amd_iommu_flush_device(iommu, bdf, DOMID_INVALID);
|
||||
}
|
||||
|
||||
if ( amd_iommu_reserve_domain_unity_map(
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
From 7e48562bf34e90f907491a0595782d2daa1ff3ad Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Thu, 14 Sep 2023 13:22:50 +0100
|
||||
Subject: [PATCH 01/11] libfsimage/xfs: Remove dead code
|
||||
|
||||
xfs_info.agnolog (and related code) and XFS_INO_AGBNO_BITS are dead code
|
||||
that serve no purpose.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
---
|
||||
tools/libfsimage/xfs/fsys_xfs.c | 18 ------------------
|
||||
1 file changed, 18 deletions(-)
|
||||
|
||||
diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
index d735a88e55f3..2800699f5985 100644
|
||||
--- a/tools/libfsimage/xfs/fsys_xfs.c
|
||||
+++ b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
@@ -37,7 +37,6 @@ struct xfs_info {
|
||||
int blklog;
|
||||
int inopblog;
|
||||
int agblklog;
|
||||
- int agnolog;
|
||||
unsigned int nextents;
|
||||
xfs_daddr_t next;
|
||||
xfs_daddr_t daddr;
|
||||
@@ -65,9 +64,7 @@ static struct xfs_info xfs;
|
||||
|
||||
#define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1))
|
||||
#define XFS_INO_OFFSET_BITS xfs.inopblog
|
||||
-#define XFS_INO_AGBNO_BITS xfs.agblklog
|
||||
#define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog)
|
||||
-#define XFS_INO_AGNO_BITS xfs.agnolog
|
||||
|
||||
static inline xfs_agblock_t
|
||||
agino2agbno (xfs_agino_t agino)
|
||||
@@ -149,20 +146,6 @@ xt_len (xfs_bmbt_rec_32_t *r)
|
||||
return le32(r->l3) & mask32lo(21);
|
||||
}
|
||||
|
||||
-static inline int
|
||||
-xfs_highbit32(xfs_uint32_t v)
|
||||
-{
|
||||
- int i;
|
||||
-
|
||||
- if (--v) {
|
||||
- for (i = 0; i < 31; i++, v >>= 1) {
|
||||
- if (v == 0)
|
||||
- return i;
|
||||
- }
|
||||
- }
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int
|
||||
isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len)
|
||||
{
|
||||
@@ -472,7 +455,6 @@ xfs_mount (fsi_file_t *ffi, const char *options)
|
||||
|
||||
xfs.inopblog = super.sb_inopblog;
|
||||
xfs.agblklog = super.sb_agblklog;
|
||||
- xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount));
|
||||
|
||||
xfs.btnode_ptr0_off =
|
||||
((xfs.bsize - sizeof(xfs_btree_block_t)) /
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
From c26327795b78c93f6fa6d5d46e34f59dc4046601 Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Thu, 14 Sep 2023 13:22:51 +0100
|
||||
Subject: [PATCH 02/11] libfsimage/xfs: Amend mask32lo() to allow the value 32
|
||||
|
||||
agblklog could plausibly be 32, but that would overflow this shift.
|
||||
Perform the shift as ULL and cast to u32 at the end instead.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Acked-by: Jan Beulich <jbeulich@suse.com>
|
||||
---
|
||||
tools/libfsimage/xfs/fsys_xfs.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
index 2800699f5985..4720bb4505c8 100644
|
||||
--- a/tools/libfsimage/xfs/fsys_xfs.c
|
||||
+++ b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
@@ -60,7 +60,7 @@ static struct xfs_info xfs;
|
||||
#define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192))
|
||||
#define icore (inode->di_core)
|
||||
|
||||
-#define mask32lo(n) (((xfs_uint32_t)1 << (n)) - 1)
|
||||
+#define mask32lo(n) ((xfs_uint32_t)((1ull << (n)) - 1))
|
||||
|
||||
#define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1))
|
||||
#define XFS_INO_OFFSET_BITS xfs.inopblog
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
From 199f0538bbec052028679a55ea512437170854c9 Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Thu, 14 Sep 2023 13:22:52 +0100
|
||||
Subject: [PATCH 03/11] libfsimage/xfs: Sanity-check the superblock during
|
||||
mounts
|
||||
|
||||
Sanity-check the XFS superblock for wellformedness at the mount handler.
|
||||
This forces pygrub to abort parsing a potentially malformed filesystem and
|
||||
ensures the invariants assumed throughout the rest of the code hold.
|
||||
|
||||
Also, derive parameters from previously sanitized parameters where possible
|
||||
(rather than reading them off the superblock)
|
||||
|
||||
The code doesn't try to avoid overflowing the end of the disk, because
|
||||
that's an unlikely and benign error. Parameters used in calculations of
|
||||
xfs_daddr_t (like the root inode index) aren't in critical need of being
|
||||
sanitized.
|
||||
|
||||
The sanitization of agblklog is basically checking that no obvious
|
||||
overflows happen on agblklog, and then ensuring agblocks is contained in
|
||||
the range (2^(sb_agblklog-1), 2^sb_agblklog].
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Reported-by: Ferdinand Nölscher <noelscher@google.com>
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
---
|
||||
tools/libfsimage/xfs/fsys_xfs.c | 48 ++++++++++++++++++++++++++-------
|
||||
tools/libfsimage/xfs/xfs.h | 12 +++++++++
|
||||
2 files changed, 50 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
index 4720bb4505c8..e4eb7e1ee26f 100644
|
||||
--- a/tools/libfsimage/xfs/fsys_xfs.c
|
||||
+++ b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
@@ -17,6 +17,7 @@
|
||||
* along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
+#include <stdbool.h>
|
||||
#include <xenfsimage_grub.h>
|
||||
#include "xfs.h"
|
||||
|
||||
@@ -433,29 +434,56 @@ first_dentry (fsi_file_t *ffi, xfs_ino_t *ino)
|
||||
return next_dentry (ffi, ino);
|
||||
}
|
||||
|
||||
+static bool
|
||||
+xfs_sb_is_invalid (const xfs_sb_t *super)
|
||||
+{
|
||||
+ return (le32(super->sb_magicnum) != XFS_SB_MAGIC)
|
||||
+ || ((le16(super->sb_versionnum) & XFS_SB_VERSION_NUMBITS) !=
|
||||
+ XFS_SB_VERSION_4)
|
||||
+ || (super->sb_inodelog < XFS_SB_INODELOG_MIN)
|
||||
+ || (super->sb_inodelog > XFS_SB_INODELOG_MAX)
|
||||
+ || (super->sb_blocklog < XFS_SB_BLOCKLOG_MIN)
|
||||
+ || (super->sb_blocklog > XFS_SB_BLOCKLOG_MAX)
|
||||
+ || (super->sb_blocklog < super->sb_inodelog)
|
||||
+ || (super->sb_agblklog > XFS_SB_AGBLKLOG_MAX)
|
||||
+ || ((1ull << super->sb_agblklog) < le32(super->sb_agblocks))
|
||||
+ || (((1ull << super->sb_agblklog) >> 1) >=
|
||||
+ le32(super->sb_agblocks))
|
||||
+ || ((super->sb_blocklog + super->sb_dirblklog) >=
|
||||
+ XFS_SB_DIRBLK_NUMBITS);
|
||||
+}
|
||||
+
|
||||
static int
|
||||
xfs_mount (fsi_file_t *ffi, const char *options)
|
||||
{
|
||||
xfs_sb_t super;
|
||||
|
||||
if (!devread (ffi, 0, 0, sizeof(super), (char *)&super)
|
||||
- || (le32(super.sb_magicnum) != XFS_SB_MAGIC)
|
||||
- || ((le16(super.sb_versionnum)
|
||||
- & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) {
|
||||
+ || xfs_sb_is_invalid(&super)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- xfs.bsize = le32 (super.sb_blocksize);
|
||||
- xfs.blklog = super.sb_blocklog;
|
||||
- xfs.bdlog = xfs.blklog - SECTOR_BITS;
|
||||
+ /*
|
||||
+ * Not sanitized. It's exclusively used to generate disk addresses,
|
||||
+ * so it's not important from a security standpoint.
|
||||
+ */
|
||||
xfs.rootino = le64 (super.sb_rootino);
|
||||
- xfs.isize = le16 (super.sb_inodesize);
|
||||
- xfs.agblocks = le32 (super.sb_agblocks);
|
||||
- xfs.dirbsize = xfs.bsize << super.sb_dirblklog;
|
||||
|
||||
- xfs.inopblog = super.sb_inopblog;
|
||||
+ /*
|
||||
+ * Sanitized to be consistent with each other, only used to
|
||||
+ * generate disk addresses, so it's safe
|
||||
+ */
|
||||
+ xfs.agblocks = le32 (super.sb_agblocks);
|
||||
xfs.agblklog = super.sb_agblklog;
|
||||
|
||||
+ /* Derived from sanitized parameters */
|
||||
+ xfs.bsize = 1 << super.sb_blocklog;
|
||||
+ xfs.blklog = super.sb_blocklog;
|
||||
+ xfs.bdlog = super.sb_blocklog - SECTOR_BITS;
|
||||
+ xfs.isize = 1 << super.sb_inodelog;
|
||||
+ xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog);
|
||||
+ xfs.inopblog = super.sb_blocklog - super.sb_inodelog;
|
||||
+
|
||||
xfs.btnode_ptr0_off =
|
||||
((xfs.bsize - sizeof(xfs_btree_block_t)) /
|
||||
(sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t)))
|
||||
diff --git a/tools/libfsimage/xfs/xfs.h b/tools/libfsimage/xfs/xfs.h
|
||||
index 40699281e44d..b87e37d3d7e9 100644
|
||||
--- a/tools/libfsimage/xfs/xfs.h
|
||||
+++ b/tools/libfsimage/xfs/xfs.h
|
||||
@@ -134,6 +134,18 @@ typedef struct xfs_sb
|
||||
xfs_uint8_t sb_dummy[7]; /* padding */
|
||||
} xfs_sb_t;
|
||||
|
||||
+/* Bound taken from xfs.c in GRUB2. It doesn't exist in the spec */
|
||||
+#define XFS_SB_DIRBLK_NUMBITS 27
|
||||
+/* Implied by the XFS specification. The minimum block size is 512 octets */
|
||||
+#define XFS_SB_BLOCKLOG_MIN 9
|
||||
+/* Implied by the XFS specification. The maximum block size is 65536 octets */
|
||||
+#define XFS_SB_BLOCKLOG_MAX 16
|
||||
+/* Implied by the XFS specification. The minimum inode size is 256 octets */
|
||||
+#define XFS_SB_INODELOG_MIN 8
|
||||
+/* Implied by the XFS specification. The maximum inode size is 2048 octets */
|
||||
+#define XFS_SB_INODELOG_MAX 11
|
||||
+/* High bound for sb_agblklog */
|
||||
+#define XFS_SB_AGBLKLOG_MAX 32
|
||||
|
||||
/* those are from xfs_btree.h */
|
||||
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
From c66fd01277939634c624c8340838682d9d4fd839 Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Thu, 14 Sep 2023 13:22:53 +0100
|
||||
Subject: [PATCH 04/11] libfsimage/xfs: Add compile-time check to libfsimage
|
||||
|
||||
Adds the common tools include folder to the -I compile flags
|
||||
of libfsimage. This allows us to use:
|
||||
xen-tools/common-macros.h:BUILD_BUG_ON()
|
||||
|
||||
With it, statically assert a sanitized "blocklog - SECTOR_BITS" cannot
|
||||
underflow.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
---
|
||||
tools/libfsimage/common.mk | 2 +-
|
||||
tools/libfsimage/xfs/fsys_xfs.c | 4 +++-
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/tools/libfsimage/common.mk b/tools/libfsimage/common.mk
|
||||
index 4fc8c6679599..e4336837d045 100644
|
||||
--- a/tools/libfsimage/common.mk
|
||||
+++ b/tools/libfsimage/common.mk
|
||||
@@ -1,7 +1,7 @@
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
FSDIR := $(libdir)/xenfsimage
|
||||
-CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ -DFSIMAGE_FSDIR=\"$(FSDIR)\"
|
||||
+CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ $(CFLAGS_xeninclude) -DFSIMAGE_FSDIR=\"$(FSDIR)\"
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
LDFLAGS += -L../common/
|
||||
|
||||
diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
index e4eb7e1ee26f..4a8dd6f2397b 100644
|
||||
--- a/tools/libfsimage/xfs/fsys_xfs.c
|
||||
+++ b/tools/libfsimage/xfs/fsys_xfs.c
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <xenfsimage_grub.h>
|
||||
+#include <xen-tools/libs.h>
|
||||
#include "xfs.h"
|
||||
|
||||
#define MAX_LINK_COUNT 8
|
||||
@@ -477,9 +478,10 @@ xfs_mount (fsi_file_t *ffi, const char *options)
|
||||
xfs.agblklog = super.sb_agblklog;
|
||||
|
||||
/* Derived from sanitized parameters */
|
||||
+ BUILD_BUG_ON(XFS_SB_BLOCKLOG_MIN < SECTOR_BITS);
|
||||
+ xfs.bdlog = super.sb_blocklog - SECTOR_BITS;
|
||||
xfs.bsize = 1 << super.sb_blocklog;
|
||||
xfs.blklog = super.sb_blocklog;
|
||||
- xfs.bdlog = super.sb_blocklog - SECTOR_BITS;
|
||||
xfs.isize = 1 << super.sb_inodelog;
|
||||
xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog);
|
||||
xfs.inopblog = super.sb_blocklog - super.sb_inodelog;
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
From ad5d0db5e68e5d4e79255fa85d9cb0069bb1c5d5 Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Mon, 25 Sep 2023 18:32:21 +0100
|
||||
Subject: [PATCH 05/11] tools/pygrub: Remove unnecessary hypercall
|
||||
|
||||
There's a hypercall being issued in order to determine whether PV64 is
|
||||
supported, but since Xen 4.3 that's strictly true so it's not required.
|
||||
|
||||
Plus, this way we can avoid mapping the privcmd interface altogether in the
|
||||
depriv pygrub.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
---
|
||||
tools/pygrub/src/pygrub | 12 +-----------
|
||||
1 file changed, 1 insertion(+), 11 deletions(-)
|
||||
|
||||
diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
|
||||
index ce7ab0eb8cf3..ce4e07d3e823 100755
|
||||
--- a/tools/pygrub/src/pygrub
|
||||
+++ b/tools/pygrub/src/pygrub
|
||||
@@ -18,7 +18,6 @@ import os, sys, string, struct, tempfile, re, traceback, stat, errno
|
||||
import copy
|
||||
import logging
|
||||
import platform
|
||||
-import xen.lowlevel.xc
|
||||
|
||||
import curses, _curses, curses.textpad, curses.ascii
|
||||
import getopt
|
||||
@@ -668,14 +667,6 @@ def run_grub(file, entry, fs, cfg_args):
|
||||
|
||||
return grubcfg
|
||||
|
||||
-def supports64bitPVguest():
|
||||
- xc = xen.lowlevel.xc.xc()
|
||||
- caps = xc.xeninfo()['xen_caps'].split(" ")
|
||||
- for cap in caps:
|
||||
- if cap == "xen-3.0-x86_64":
|
||||
- return True
|
||||
- return False
|
||||
-
|
||||
# If nothing has been specified, look for a Solaris domU. If found, perform the
|
||||
# necessary tweaks.
|
||||
def sniff_solaris(fs, cfg):
|
||||
@@ -684,8 +675,7 @@ def sniff_solaris(fs, cfg):
|
||||
return cfg
|
||||
|
||||
if not cfg["kernel"]:
|
||||
- if supports64bitPVguest() and \
|
||||
- fs.file_exists("/platform/i86xpv/kernel/amd64/unix"):
|
||||
+ if fs.file_exists("/platform/i86xpv/kernel/amd64/unix"):
|
||||
cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix"
|
||||
cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive"
|
||||
elif fs.file_exists("/platform/i86xpv/kernel/unix"):
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
From d3ceb0b314005a656dd2ca4b2821575a36f8426d Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Mon, 25 Sep 2023 18:32:22 +0100
|
||||
Subject: [PATCH 06/11] tools/pygrub: Small refactors
|
||||
|
||||
Small tidy up to ensure output_directory always has a trailing '/' to ease
|
||||
concatenating paths and that `output` can only be a filename or None.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
---
|
||||
tools/pygrub/src/pygrub | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
|
||||
index ce4e07d3e823..1042c05b8676 100755
|
||||
--- a/tools/pygrub/src/pygrub
|
||||
+++ b/tools/pygrub/src/pygrub
|
||||
@@ -793,7 +793,7 @@ if __name__ == "__main__":
|
||||
debug = False
|
||||
not_really = False
|
||||
output_format = "sxp"
|
||||
- output_directory = "/var/run/xen/pygrub"
|
||||
+ output_directory = "/var/run/xen/pygrub/"
|
||||
|
||||
# what was passed in
|
||||
incfg = { "kernel": None, "ramdisk": None, "args": "" }
|
||||
@@ -815,7 +815,8 @@ if __name__ == "__main__":
|
||||
usage()
|
||||
sys.exit()
|
||||
elif o in ("--output",):
|
||||
- output = a
|
||||
+ if a != "-":
|
||||
+ output = a
|
||||
elif o in ("--kernel",):
|
||||
incfg["kernel"] = a
|
||||
elif o in ("--ramdisk",):
|
||||
@@ -847,12 +848,11 @@ if __name__ == "__main__":
|
||||
if not os.path.isdir(a):
|
||||
print("%s is not an existing directory" % a)
|
||||
sys.exit(1)
|
||||
- output_directory = a
|
||||
+ output_directory = a + '/'
|
||||
|
||||
if debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
-
|
||||
try:
|
||||
os.makedirs(output_directory, 0o700)
|
||||
except OSError as e:
|
||||
@@ -861,7 +861,7 @@ if __name__ == "__main__":
|
||||
else:
|
||||
raise
|
||||
|
||||
- if output is None or output == "-":
|
||||
+ if output is None:
|
||||
fd = sys.stdout.fileno()
|
||||
else:
|
||||
fd = os.open(output, os.O_WRONLY)
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
From 9e80cfecde338cea0db136c2fb5ed78d6081e05f Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Mon, 25 Sep 2023 18:32:23 +0100
|
||||
Subject: [PATCH 07/11] tools/pygrub: Open the output files earlier
|
||||
|
||||
This patch allows pygrub to get ahold of every RW file descriptor it needs
|
||||
early on. A later patch will clamp the filesystem it can access so it can't
|
||||
obtain any others.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
---
|
||||
tools/pygrub/src/pygrub | 37 ++++++++++++++++++++++---------------
|
||||
1 file changed, 22 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
|
||||
index 1042c05b8676..91e2ec2ab105 100755
|
||||
--- a/tools/pygrub/src/pygrub
|
||||
+++ b/tools/pygrub/src/pygrub
|
||||
@@ -738,8 +738,7 @@ if __name__ == "__main__":
|
||||
def usage():
|
||||
print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] <image>" %(sys.argv[0],), file=sys.stderr)
|
||||
|
||||
- def copy_from_image(fs, file_to_read, file_type, output_directory,
|
||||
- not_really):
|
||||
+ def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really):
|
||||
if not_really:
|
||||
if fs.file_exists(file_to_read):
|
||||
return "<%s:%s>" % (file_type, file_to_read)
|
||||
@@ -750,21 +749,18 @@ if __name__ == "__main__":
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit("Error opening %s in guest" % file_to_read)
|
||||
- (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".",
|
||||
- dir=output_directory)
|
||||
dataoff = 0
|
||||
while True:
|
||||
data = datafile.read(FS_READ_MAX, dataoff)
|
||||
if len(data) == 0:
|
||||
- os.close(tfd)
|
||||
+ os.close(fd_dst)
|
||||
del datafile
|
||||
- return ret
|
||||
+ return
|
||||
try:
|
||||
- os.write(tfd, data)
|
||||
+ os.write(fd_dst, data)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
- os.close(tfd)
|
||||
- os.unlink(ret)
|
||||
+ os.unlink(path_dst)
|
||||
del datafile
|
||||
sys.exit("Error writing temporary copy of "+file_type)
|
||||
dataoff += len(data)
|
||||
@@ -861,6 +857,14 @@ if __name__ == "__main__":
|
||||
else:
|
||||
raise
|
||||
|
||||
+ if not_really:
|
||||
+ fd_kernel = path_kernel = fd_ramdisk = path_ramdisk = None
|
||||
+ else:
|
||||
+ (fd_kernel, path_kernel) = tempfile.mkstemp(prefix="boot_kernel.",
|
||||
+ dir=output_directory)
|
||||
+ (fd_ramdisk, path_ramdisk) = tempfile.mkstemp(prefix="boot_ramdisk.",
|
||||
+ dir=output_directory)
|
||||
+
|
||||
if output is None:
|
||||
fd = sys.stdout.fileno()
|
||||
else:
|
||||
@@ -920,20 +924,23 @@ if __name__ == "__main__":
|
||||
if fs is None:
|
||||
raise RuntimeError("Unable to find partition containing kernel")
|
||||
|
||||
- bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel",
|
||||
- output_directory, not_really)
|
||||
+ copy_from_image(fs, chosencfg["kernel"], "kernel",
|
||||
+ fd_kernel, path_kernel, not_really)
|
||||
+ bootcfg["kernel"] = path_kernel
|
||||
|
||||
if chosencfg["ramdisk"]:
|
||||
try:
|
||||
- bootcfg["ramdisk"] = copy_from_image(fs, chosencfg["ramdisk"],
|
||||
- "ramdisk", output_directory,
|
||||
- not_really)
|
||||
+ copy_from_image(fs, chosencfg["ramdisk"], "ramdisk",
|
||||
+ fd_ramdisk, path_ramdisk, not_really)
|
||||
except:
|
||||
if not not_really:
|
||||
- os.unlink(bootcfg["kernel"])
|
||||
+ os.unlink(path_kernel)
|
||||
raise
|
||||
+ bootcfg["ramdisk"] = path_ramdisk
|
||||
else:
|
||||
initrd = None
|
||||
+ if not not_really:
|
||||
+ os.unlink(path_ramdisk)
|
||||
|
||||
args = None
|
||||
if chosencfg["args"]:
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
From 2fb4cdcedd8720f78c4bd44739a5d30dd1a7d9a5 Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Mon, 25 Sep 2023 18:32:24 +0100
|
||||
Subject: [PATCH 08/11] tools/libfsimage: Export a new function to preload all
|
||||
plugins
|
||||
|
||||
This is work required in order to let pygrub operate in highly deprivileged
|
||||
chroot mode. This patch adds a function that preloads every plugin, hence
|
||||
ensuring that a on function exit, every shared library is loaded in memory.
|
||||
|
||||
The new "init" function is supposed to be used before depriv, but that's
|
||||
fine because it's not acting on untrusted data.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
---
|
||||
tools/libfsimage/common/fsimage_plugin.c | 4 ++--
|
||||
tools/libfsimage/common/mapfile-GNU | 1 +
|
||||
tools/libfsimage/common/mapfile-SunOS | 1 +
|
||||
tools/libfsimage/common/xenfsimage.h | 8 ++++++++
|
||||
tools/pygrub/src/fsimage/fsimage.c | 15 +++++++++++++++
|
||||
5 files changed, 27 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c
|
||||
index de1412b4233a..d0cb9e96a654 100644
|
||||
--- a/tools/libfsimage/common/fsimage_plugin.c
|
||||
+++ b/tools/libfsimage/common/fsimage_plugin.c
|
||||
@@ -119,7 +119,7 @@ fail:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
-static int load_plugins(void)
|
||||
+int fsi_init(void)
|
||||
{
|
||||
const char *fsdir = getenv("XEN_FSIMAGE_FSDIR");
|
||||
struct dirent *dp = NULL;
|
||||
@@ -180,7 +180,7 @@ int find_plugin(fsi_t *fsi, const char *path, const char *options)
|
||||
fsi_plugin_t *fp;
|
||||
int ret = 0;
|
||||
|
||||
- if (plugins == NULL && (ret = load_plugins()) != 0)
|
||||
+ if (plugins == NULL && (ret = fsi_init()) != 0)
|
||||
goto out;
|
||||
|
||||
for (fp = plugins; fp != NULL; fp = fp->fp_next) {
|
||||
diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU
|
||||
index 26d4d7a69ec7..2d54d527d7f5 100644
|
||||
--- a/tools/libfsimage/common/mapfile-GNU
|
||||
+++ b/tools/libfsimage/common/mapfile-GNU
|
||||
@@ -1,6 +1,7 @@
|
||||
VERSION {
|
||||
libfsimage.so.1.0 {
|
||||
global:
|
||||
+ fsi_init;
|
||||
fsi_open_fsimage;
|
||||
fsi_close_fsimage;
|
||||
fsi_file_exists;
|
||||
diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS
|
||||
index e99b90b65077..48deedb4252f 100644
|
||||
--- a/tools/libfsimage/common/mapfile-SunOS
|
||||
+++ b/tools/libfsimage/common/mapfile-SunOS
|
||||
@@ -1,5 +1,6 @@
|
||||
libfsimage.so.1.0 {
|
||||
global:
|
||||
+ fsi_init;
|
||||
fsi_open_fsimage;
|
||||
fsi_close_fsimage;
|
||||
fsi_file_exists;
|
||||
diff --git a/tools/libfsimage/common/xenfsimage.h b/tools/libfsimage/common/xenfsimage.h
|
||||
index 201abd54f23a..341883b2d71a 100644
|
||||
--- a/tools/libfsimage/common/xenfsimage.h
|
||||
+++ b/tools/libfsimage/common/xenfsimage.h
|
||||
@@ -35,6 +35,14 @@ extern C {
|
||||
typedef struct fsi fsi_t;
|
||||
typedef struct fsi_file fsi_file_t;
|
||||
|
||||
+/*
|
||||
+ * Optional initialization function. If invoked it loads the associated
|
||||
+ * dynamic libraries for the backends ahead of time. This is required if
|
||||
+ * the library is to run as part of a highly deprivileged executable, as
|
||||
+ * the libraries may not be reachable after depriv.
|
||||
+ */
|
||||
+int fsi_init(void);
|
||||
+
|
||||
fsi_t *fsi_open_fsimage(const char *, uint64_t, const char *);
|
||||
void fsi_close_fsimage(fsi_t *);
|
||||
|
||||
diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c
|
||||
index 2ebbbe35df92..92fbf2851f01 100644
|
||||
--- a/tools/pygrub/src/fsimage/fsimage.c
|
||||
+++ b/tools/pygrub/src/fsimage/fsimage.c
|
||||
@@ -286,6 +286,15 @@ fsimage_getbootstring(PyObject *o, PyObject *args)
|
||||
return Py_BuildValue("s", bootstring);
|
||||
}
|
||||
|
||||
+static PyObject *
|
||||
+fsimage_init(PyObject *o, PyObject *args)
|
||||
+{
|
||||
+ if (!PyArg_ParseTuple(args, ""))
|
||||
+ return (NULL);
|
||||
+
|
||||
+ return Py_BuildValue("i", fsi_init());
|
||||
+}
|
||||
+
|
||||
PyDoc_STRVAR(fsimage_open__doc__,
|
||||
"open(name, [offset=off]) - Open the given file as a filesystem image.\n"
|
||||
"\n"
|
||||
@@ -297,7 +306,13 @@ PyDoc_STRVAR(fsimage_getbootstring__doc__,
|
||||
"getbootstring(fs) - Return the boot string needed for this file system "
|
||||
"or NULL if none is needed.\n");
|
||||
|
||||
+PyDoc_STRVAR(fsimage_init__doc__,
|
||||
+ "init() - Loads every dynamic library contained in xenfsimage "
|
||||
+ "into memory so that it can be used in chrooted environments.\n");
|
||||
+
|
||||
static struct PyMethodDef fsimage_module_methods[] = {
|
||||
+ { "init", (PyCFunction)fsimage_init,
|
||||
+ METH_VARARGS, fsimage_init__doc__ },
|
||||
{ "open", (PyCFunction)fsimage_open,
|
||||
METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ },
|
||||
{ "getbootstring", (PyCFunction)fsimage_getbootstring,
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
From 150771ce86a07e469e34941a63c56e2cf242223b Mon Sep 17 00:00:00 2001
|
||||
From: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Date: Mon, 25 Sep 2023 18:32:25 +0100
|
||||
Subject: [PATCH 09/11] tools/pygrub: Deprivilege pygrub
|
||||
|
||||
Introduce a --runas=<uid> flag to deprivilege pygrub on Linux and *BSDs. It
|
||||
also implicitly creates a chroot env where it drops a deprivileged forked
|
||||
process. The chroot itself is cleaned up at the end.
|
||||
|
||||
If the --runas arg is present, then pygrub forks, leaving the child to
|
||||
deprivilege itself, and waiting for it to complete. When the child exists,
|
||||
the parent performs cleanup and exits with the same error code.
|
||||
|
||||
This is roughly what the child does:
|
||||
1. Initialize libfsimage (this loads every .so in memory so the chroot
|
||||
can avoid bind-mounting /{,usr}/lib*
|
||||
2. Create a temporary empty chroot directory
|
||||
3. Mount tmpfs in it
|
||||
4. Bind mount the disk inside, because libfsimage expects a path, not a
|
||||
file descriptor.
|
||||
5. Remount the root tmpfs to be stricter (ro,nosuid,nodev)
|
||||
6. Set RLIMIT_FSIZE to a sensibly high amount (128 MiB)
|
||||
7. Depriv gid, groups and uid
|
||||
|
||||
With this scheme in place, the "output" files are writable (up to
|
||||
RLIMIT_FSIZE octets) and the exposed filesystem is immutable and contains
|
||||
the single only file we can't easily get rid of (the disk).
|
||||
|
||||
If running on Linux, the child process also unshares mount, IPC, and
|
||||
network namespaces before dropping its privileges.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
|
||||
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
---
|
||||
tools/pygrub/setup.py | 2 +-
|
||||
tools/pygrub/src/pygrub | 162 +++++++++++++++++++++++++++++++++++++---
|
||||
2 files changed, 154 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py
|
||||
index 0e4e3d02d372..06b96733d020 100644
|
||||
--- a/tools/pygrub/setup.py
|
||||
+++ b/tools/pygrub/setup.py
|
||||
@@ -17,7 +17,7 @@ xenfsimage = Extension("xenfsimage",
|
||||
pkgs = [ 'grub' ]
|
||||
|
||||
setup(name='pygrub',
|
||||
- version='0.6',
|
||||
+ version='0.7',
|
||||
description='Boot loader that looks a lot like grub for Xen',
|
||||
author='Jeremy Katz',
|
||||
author_email='katzj@redhat.com',
|
||||
diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
|
||||
index 91e2ec2ab105..7cea496ade08 100755
|
||||
--- a/tools/pygrub/src/pygrub
|
||||
+++ b/tools/pygrub/src/pygrub
|
||||
@@ -16,8 +16,11 @@ from __future__ import print_function
|
||||
|
||||
import os, sys, string, struct, tempfile, re, traceback, stat, errno
|
||||
import copy
|
||||
+import ctypes, ctypes.util
|
||||
import logging
|
||||
import platform
|
||||
+import resource
|
||||
+import subprocess
|
||||
|
||||
import curses, _curses, curses.textpad, curses.ascii
|
||||
import getopt
|
||||
@@ -27,10 +30,135 @@ import grub.GrubConf
|
||||
import grub.LiloConf
|
||||
import grub.ExtLinuxConf
|
||||
|
||||
-PYGRUB_VER = 0.6
|
||||
+PYGRUB_VER = 0.7
|
||||
FS_READ_MAX = 1024 * 1024
|
||||
SECTOR_SIZE = 512
|
||||
|
||||
+# Unless provided through the env variable PYGRUB_MAX_FILE_SIZE_MB, then
|
||||
+# this is the maximum filesize allowed for files written by the depriv
|
||||
+# pygrub
|
||||
+LIMIT_FSIZE = 128 << 20
|
||||
+
|
||||
+CLONE_NEWNS = 0x00020000 # mount namespace
|
||||
+CLONE_NEWNET = 0x40000000 # network namespace
|
||||
+CLONE_NEWIPC = 0x08000000 # IPC namespace
|
||||
+
|
||||
+def unshare(flags):
|
||||
+ if not sys.platform.startswith("linux"):
|
||||
+ print("skip_unshare reason=not_linux platform=%s", sys.platform, file=sys.stderr)
|
||||
+ return
|
||||
+
|
||||
+ libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
|
||||
+ unshare_prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True)
|
||||
+ unshare = unshare_prototype(('unshare', libc))
|
||||
+
|
||||
+ if unshare(flags) < 0:
|
||||
+ raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
|
||||
+
|
||||
+def bind_mount(src, dst, options):
|
||||
+ open(dst, "a").close() # touch
|
||||
+
|
||||
+ rc = subprocess.call(["mount", "--bind", "-o", options, src, dst])
|
||||
+ if rc != 0:
|
||||
+ raise RuntimeError("bad_mount: src=%s dst=%s opts=%s" %
|
||||
+ (src, dst, options))
|
||||
+
|
||||
+def downgrade_rlimits():
|
||||
+ # Wipe the authority to use unrequired resources
|
||||
+ resource.setrlimit(resource.RLIMIT_NPROC, (0, 0))
|
||||
+ resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
|
||||
+ resource.setrlimit(resource.RLIMIT_MEMLOCK, (0, 0))
|
||||
+
|
||||
+ # py2's resource module doesn't know about resource.RLIMIT_MSGQUEUE
|
||||
+ #
|
||||
+ # TODO: Use resource.RLIMIT_MSGQUEUE after python2 is deprecated
|
||||
+ if sys.platform.startswith('linux'):
|
||||
+ RLIMIT_MSGQUEUE = 12
|
||||
+ resource.setrlimit(RLIMIT_MSGQUEUE, (0, 0))
|
||||
+
|
||||
+ # The final look of the filesystem for this process is fully RO, but
|
||||
+ # note we have some file descriptor already open (notably, kernel and
|
||||
+ # ramdisk). In order to avoid a compromised pygrub from filling up the
|
||||
+ # filesystem we set RLIMIT_FSIZE to a high bound, so that the file
|
||||
+ # write permissions are bound.
|
||||
+ fsize = LIMIT_FSIZE
|
||||
+ if "PYGRUB_MAX_FILE_SIZE_MB" in os.environ.keys():
|
||||
+ fsize = os.environ["PYGRUB_MAX_FILE_SIZE_MB"] << 20
|
||||
+
|
||||
+ resource.setrlimit(resource.RLIMIT_FSIZE, (fsize, fsize))
|
||||
+
|
||||
+def depriv(output_directory, output, device, uid, path_kernel, path_ramdisk):
|
||||
+ # The only point of this call is to force the loading of libfsimage.
|
||||
+ # That way, we don't need to bind-mount it into the chroot
|
||||
+ rc = xenfsimage.init()
|
||||
+ if rc != 0:
|
||||
+ os.unlink(path_ramdisk)
|
||||
+ os.unlink(path_kernel)
|
||||
+ raise RuntimeError("bad_xenfsimage: rc=%d" % rc)
|
||||
+
|
||||
+ # Create a temporary directory for the chroot
|
||||
+ chroot = tempfile.mkdtemp(prefix=str(uid)+'-', dir=output_directory) + '/'
|
||||
+ device_path = '/device'
|
||||
+
|
||||
+ pid = os.fork()
|
||||
+ if pid:
|
||||
+ # parent
|
||||
+ _, rc = os.waitpid(pid, 0)
|
||||
+
|
||||
+ for path in [path_kernel, path_ramdisk]:
|
||||
+ # If the child didn't write anything, just get rid of it,
|
||||
+ # otherwise we end up consuming a 0-size file when parsing
|
||||
+ # systems without a ramdisk that the ultimate caller of pygrub
|
||||
+ # may just be unaware of
|
||||
+ if rc != 0 or os.path.getsize(path) == 0:
|
||||
+ os.unlink(path)
|
||||
+
|
||||
+ # Normally, unshare(CLONE_NEWNS) will ensure this is not required.
|
||||
+ # However, this syscall doesn't exist in *BSD systems and doesn't
|
||||
+ # auto-unmount everything on older Linux kernels (At least as of
|
||||
+ # Linux 4.19, but it seems fixed in 5.15). Either way,
|
||||
+ # recursively unmount everything if needed. Quietly.
|
||||
+ with open('/dev/null', 'w') as devnull:
|
||||
+ subprocess.call(["umount", "-f", chroot + device_path],
|
||||
+ stdout=devnull, stderr=devnull)
|
||||
+ subprocess.call(["umount", "-f", chroot],
|
||||
+ stdout=devnull, stderr=devnull)
|
||||
+ os.rmdir(chroot)
|
||||
+
|
||||
+ sys.exit(rc)
|
||||
+
|
||||
+ # By unsharing the namespace we're making sure it's all bulk-released
|
||||
+ # at the end, when the namespaces disappear. This means the kernel does
|
||||
+ # (almost) all the cleanup for us and the parent just has to remove the
|
||||
+ # temporary directory.
|
||||
+ unshare(CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWNET)
|
||||
+
|
||||
+ # Set sensible limits using the setrlimit interface
|
||||
+ downgrade_rlimits()
|
||||
+
|
||||
+ # We'll mount tmpfs on the chroot to ensure the deprivileged child
|
||||
+ # cannot affect the persistent state. It's RW now in order to
|
||||
+ # bind-mount the device, but note it's remounted RO after that.
|
||||
+ rc = subprocess.call(["mount", "-t", "tmpfs", "none", chroot])
|
||||
+ if rc != 0:
|
||||
+ raise RuntimeError("mount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot))
|
||||
+
|
||||
+ # Bind the untrusted device RO
|
||||
+ bind_mount(device, chroot + device_path, "ro,nosuid,noexec")
|
||||
+
|
||||
+ rc = subprocess.call(["mount", "-t", "tmpfs", "-o", "remount,ro,nosuid,noexec,nodev", "none", chroot])
|
||||
+ if rc != 0:
|
||||
+ raise RuntimeError("remount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot))
|
||||
+
|
||||
+ # Drop superpowers!
|
||||
+ os.chroot(chroot)
|
||||
+ os.chdir('/')
|
||||
+ os.setgid(uid)
|
||||
+ os.setgroups([uid])
|
||||
+ os.setuid(uid)
|
||||
+
|
||||
+ return device_path
|
||||
+
|
||||
def read_size_roundup(fd, size):
|
||||
if platform.system() != 'FreeBSD':
|
||||
return size
|
||||
@@ -736,7 +864,7 @@ if __name__ == "__main__":
|
||||
sel = None
|
||||
|
||||
def usage():
|
||||
- print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] <image>" %(sys.argv[0],), file=sys.stderr)
|
||||
+ print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--runas=] [--offset=] <image>" %(sys.argv[0],), file=sys.stderr)
|
||||
|
||||
def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really):
|
||||
if not_really:
|
||||
@@ -760,7 +888,8 @@ if __name__ == "__main__":
|
||||
os.write(fd_dst, data)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
- os.unlink(path_dst)
|
||||
+ if path_dst:
|
||||
+ os.unlink(path_dst)
|
||||
del datafile
|
||||
sys.exit("Error writing temporary copy of "+file_type)
|
||||
dataoff += len(data)
|
||||
@@ -769,7 +898,7 @@ if __name__ == "__main__":
|
||||
opts, args = getopt.gnu_getopt(sys.argv[1:], 'qilnh::',
|
||||
["quiet", "interactive", "list-entries", "not-really", "help",
|
||||
"output=", "output-format=", "output-directory=", "offset=",
|
||||
- "entry=", "kernel=",
|
||||
+ "runas=", "entry=", "kernel=",
|
||||
"ramdisk=", "args=", "isconfig", "debug"])
|
||||
except getopt.GetoptError:
|
||||
usage()
|
||||
@@ -790,6 +919,7 @@ if __name__ == "__main__":
|
||||
not_really = False
|
||||
output_format = "sxp"
|
||||
output_directory = "/var/run/xen/pygrub/"
|
||||
+ uid = None
|
||||
|
||||
# what was passed in
|
||||
incfg = { "kernel": None, "ramdisk": None, "args": "" }
|
||||
@@ -813,6 +943,13 @@ if __name__ == "__main__":
|
||||
elif o in ("--output",):
|
||||
if a != "-":
|
||||
output = a
|
||||
+ elif o in ("--runas",):
|
||||
+ try:
|
||||
+ uid = int(a)
|
||||
+ except ValueError:
|
||||
+ print("runas value must be an integer user id")
|
||||
+ usage()
|
||||
+ sys.exit(1)
|
||||
elif o in ("--kernel",):
|
||||
incfg["kernel"] = a
|
||||
elif o in ("--ramdisk",):
|
||||
@@ -849,6 +986,10 @@ if __name__ == "__main__":
|
||||
if debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
+ if interactive and uid:
|
||||
+ print("In order to use --runas, you must also set --entry or -q", file=sys.stderr)
|
||||
+ sys.exit(1)
|
||||
+
|
||||
try:
|
||||
os.makedirs(output_directory, 0o700)
|
||||
except OSError as e:
|
||||
@@ -870,6 +1011,9 @@ if __name__ == "__main__":
|
||||
else:
|
||||
fd = os.open(output, os.O_WRONLY)
|
||||
|
||||
+ if uid:
|
||||
+ file = depriv(output_directory, output, file, uid, path_kernel, path_ramdisk)
|
||||
+
|
||||
# debug
|
||||
if isconfig:
|
||||
chosencfg = run_grub(file, entry, fs, incfg["args"])
|
||||
@@ -925,21 +1069,21 @@ if __name__ == "__main__":
|
||||
raise RuntimeError("Unable to find partition containing kernel")
|
||||
|
||||
copy_from_image(fs, chosencfg["kernel"], "kernel",
|
||||
- fd_kernel, path_kernel, not_really)
|
||||
+ fd_kernel, None if uid else path_kernel, not_really)
|
||||
bootcfg["kernel"] = path_kernel
|
||||
|
||||
if chosencfg["ramdisk"]:
|
||||
try:
|
||||
copy_from_image(fs, chosencfg["ramdisk"], "ramdisk",
|
||||
- fd_ramdisk, path_ramdisk, not_really)
|
||||
+ fd_ramdisk, None if uid else path_ramdisk, not_really)
|
||||
except:
|
||||
- if not not_really:
|
||||
- os.unlink(path_kernel)
|
||||
+ if not uid and not not_really:
|
||||
+ os.unlink(path_kernel)
|
||||
raise
|
||||
bootcfg["ramdisk"] = path_ramdisk
|
||||
else:
|
||||
initrd = None
|
||||
- if not not_really:
|
||||
+ if not uid and not not_really:
|
||||
os.unlink(path_ramdisk)
|
||||
|
||||
args = None
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
From 698b451473a6d868ca0f60a124fc4f31d81cd7b1 Mon Sep 17 00:00:00 2001
|
||||
From: Roger Pau Monne <roger.pau@citrix.com>
|
||||
Date: Mon, 25 Sep 2023 14:30:20 +0200
|
||||
Subject: [PATCH 10/11] libxl: add support for running bootloader in restricted
|
||||
mode
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Much like the device model depriv mode, add the same kind of support for the
|
||||
bootloader. Such feature allows passing a UID as a parameter for the
|
||||
bootloader to run as, together with the bootloader itself taking the necessary
|
||||
actions to isolate.
|
||||
|
||||
Note that the user to run the bootloader as must have the right permissions to
|
||||
access the guest disk image (in read mode only), and that the bootloader will
|
||||
be run in non-interactive mode when restricted.
|
||||
|
||||
If enabled bootloader restrict mode will attempt to re-use the user(s) from the
|
||||
QEMU depriv implementation if no user is provided on the configuration file or
|
||||
the environment. See docs/features/qemu-deprivilege.pandoc for more
|
||||
information about how to setup those users.
|
||||
|
||||
Bootloader restrict mode is not enabled by default as it requires certain
|
||||
setup to be done first (setup of the user(s) to use in restrict mode).
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
|
||||
---
|
||||
docs/man/xl.1.pod.in | 33 +++++++++++
|
||||
tools/libs/light/libxl_bootloader.c | 89 ++++++++++++++++++++++++++++-
|
||||
tools/libs/light/libxl_dm.c | 8 +--
|
||||
tools/libs/light/libxl_internal.h | 8 +++
|
||||
4 files changed, 131 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in
|
||||
index 101e14241d1c..4831e122427d 100644
|
||||
--- a/docs/man/xl.1.pod.in
|
||||
+++ b/docs/man/xl.1.pod.in
|
||||
@@ -1957,6 +1957,39 @@ ignored:
|
||||
|
||||
=back
|
||||
|
||||
+=head1 ENVIRONMENT VARIABLES
|
||||
+
|
||||
+The following environment variables shall affect the execution of xl:
|
||||
+
|
||||
+=over 4
|
||||
+
|
||||
+=item LIBXL_BOOTLOADER_RESTRICT
|
||||
+
|
||||
+Attempt to restrict the bootloader after startup, to limit the
|
||||
+consequences of security vulnerabilities due to parsing guest
|
||||
+owned image files.
|
||||
+
|
||||
+See docs/features/qemu-deprivilege.pandoc for more information
|
||||
+on how to setup the unprivileged users.
|
||||
+
|
||||
+Note that running the bootloader in restricted mode also implies using
|
||||
+non-interactive mode, and the disk image must be readable by the
|
||||
+restricted user.
|
||||
+
|
||||
+Having this variable set is equivalent to enabling the option, even if the
|
||||
+value is 0.
|
||||
+
|
||||
+=item LIBXL_BOOTLOADER_USER
|
||||
+
|
||||
+When using bootloader_restrict, run the bootloader as this user. If
|
||||
+not set the default QEMU restrict users will be used.
|
||||
+
|
||||
+NOTE: Each domain MUST have a SEPARATE username.
|
||||
+
|
||||
+See docs/features/qemu-deprivilege.pandoc for more information.
|
||||
+
|
||||
+=back
|
||||
+
|
||||
=head1 SEE ALSO
|
||||
|
||||
The following man pages:
|
||||
diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c
|
||||
index 108329b4a5bb..23c0ef3e8935 100644
|
||||
--- a/tools/libs/light/libxl_bootloader.c
|
||||
+++ b/tools/libs/light/libxl_bootloader.c
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "libxl_osdeps.h" /* must come before any other headers */
|
||||
|
||||
+#include <pwd.h>
|
||||
#include <termios.h>
|
||||
#ifdef HAVE_UTMP_H
|
||||
#include <utmp.h>
|
||||
@@ -42,8 +43,71 @@ static void bootloader_arg(libxl__bootloader_state *bl, const char *arg)
|
||||
bl->args[bl->nargs++] = arg;
|
||||
}
|
||||
|
||||
-static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
|
||||
- const char *bootloader_path)
|
||||
+static int bootloader_uid(libxl__gc *gc, domid_t guest_domid,
|
||||
+ const char *user, uid_t *intended_uid)
|
||||
+{
|
||||
+ struct passwd *user_base, user_pwbuf;
|
||||
+ int rc;
|
||||
+
|
||||
+ if (user) {
|
||||
+ rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
|
||||
+ if (rc) return rc;
|
||||
+
|
||||
+ if (!user_base) {
|
||||
+ LOGD(ERROR, guest_domid, "Couldn't find user %s", user);
|
||||
+ return ERROR_INVAL;
|
||||
+ }
|
||||
+
|
||||
+ *intended_uid = user_base->pw_uid;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* Re-use QEMU user range for the bootloader. */
|
||||
+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
|
||||
+ &user_pwbuf, &user_base);
|
||||
+ if (rc) return rc;
|
||||
+
|
||||
+ if (user_base) {
|
||||
+ struct passwd *user_clash, user_clash_pwbuf;
|
||||
+ uid_t temp_uid = user_base->pw_uid + guest_domid;
|
||||
+
|
||||
+ rc = userlookup_helper_getpwuid(gc, temp_uid, &user_clash_pwbuf,
|
||||
+ &user_clash);
|
||||
+ if (rc) return rc;
|
||||
+
|
||||
+ if (user_clash) {
|
||||
+ LOGD(ERROR, guest_domid,
|
||||
+ "wanted to use uid %ld (%s + %d) but that is user %s !",
|
||||
+ (long)temp_uid, LIBXL_QEMU_USER_RANGE_BASE,
|
||||
+ guest_domid, user_clash->pw_name);
|
||||
+ return ERROR_INVAL;
|
||||
+ }
|
||||
+
|
||||
+ *intended_uid = temp_uid;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_SHARED, &user_pwbuf,
|
||||
+ &user_base);
|
||||
+ if (rc) return rc;
|
||||
+
|
||||
+ if (user_base) {
|
||||
+ LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s",
|
||||
+ LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED);
|
||||
+ *intended_uid = user_base->pw_uid;
|
||||
+
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ LOGD(ERROR, guest_domid,
|
||||
+ "Could not find user %s or range base pseudo-user %s, cannot restrict",
|
||||
+ LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE);
|
||||
+
|
||||
+ return ERROR_INVAL;
|
||||
+}
|
||||
+
|
||||
+static int make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
|
||||
+ const char *bootloader_path)
|
||||
{
|
||||
const libxl_domain_build_info *info = bl->info;
|
||||
|
||||
@@ -61,6 +125,23 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
|
||||
ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk));
|
||||
if (info->cmdline && *info->cmdline != '\0')
|
||||
ARG(GCSPRINTF("--args=%s", info->cmdline));
|
||||
+ if (getenv("LIBXL_BOOTLOADER_RESTRICT") ||
|
||||
+ getenv("LIBXL_BOOTLOADER_USER")) {
|
||||
+ uid_t uid = -1;
|
||||
+ int rc = bootloader_uid(gc, bl->domid, getenv("LIBXL_BOOTLOADER_USER"),
|
||||
+ &uid);
|
||||
+
|
||||
+ if (rc) return rc;
|
||||
+
|
||||
+ assert(uid != -1);
|
||||
+ if (!uid) {
|
||||
+ LOGD(ERROR, bl->domid, "bootloader restrict UID is 0 (root)!");
|
||||
+ return ERROR_INVAL;
|
||||
+ }
|
||||
+ LOGD(DEBUG, bl->domid, "using uid %ld", (long)uid);
|
||||
+ ARG(GCSPRINTF("--runas=%ld", (long)uid));
|
||||
+ ARG("--quiet");
|
||||
+ }
|
||||
|
||||
ARG(GCSPRINTF("--output=%s", bl->outputpath));
|
||||
ARG("--output-format=simple0");
|
||||
@@ -79,6 +160,7 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
|
||||
/* Sentinel for execv */
|
||||
ARG(NULL);
|
||||
|
||||
+ return 0;
|
||||
#undef ARG
|
||||
}
|
||||
|
||||
@@ -443,7 +525,8 @@ static void bootloader_disk_attached_cb(libxl__egc *egc,
|
||||
bootloader = bltmp;
|
||||
}
|
||||
|
||||
- make_bootloader_args(gc, bl, bootloader);
|
||||
+ rc = make_bootloader_args(gc, bl, bootloader);
|
||||
+ if (rc) goto out;
|
||||
|
||||
bl->openpty.ao = ao;
|
||||
bl->openpty.callback = bootloader_gotptys;
|
||||
diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c
|
||||
index fc264a3a13a6..14b593110f7c 100644
|
||||
--- a/tools/libs/light/libxl_dm.c
|
||||
+++ b/tools/libs/light/libxl_dm.c
|
||||
@@ -80,10 +80,10 @@ static int libxl__create_qemu_logfile(libxl__gc *gc, char *name)
|
||||
* On error, return a libxl-style error code.
|
||||
*/
|
||||
#define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF) \
|
||||
- static int userlookup_helper_##NAME(libxl__gc *gc, \
|
||||
- SPEC_TYPE spec, \
|
||||
- struct STRUCTNAME *resultbuf, \
|
||||
- struct STRUCTNAME **out) \
|
||||
+ int userlookup_helper_##NAME(libxl__gc *gc, \
|
||||
+ SPEC_TYPE spec, \
|
||||
+ struct STRUCTNAME *resultbuf, \
|
||||
+ struct STRUCTNAME **out) \
|
||||
{ \
|
||||
struct STRUCTNAME *resultp = NULL; \
|
||||
char *buf = NULL; \
|
||||
diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h
|
||||
index 7ad38de30e0b..f1e3a9a15b13 100644
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -4873,6 +4873,14 @@ struct libxl__cpu_policy {
|
||||
struct xc_msr *msr;
|
||||
};
|
||||
|
||||
+struct passwd;
|
||||
+_hidden int userlookup_helper_getpwnam(libxl__gc*, const char *user,
|
||||
+ struct passwd *res,
|
||||
+ struct passwd **out);
|
||||
+_hidden int userlookup_helper_getpwuid(libxl__gc*, uid_t uid,
|
||||
+ struct passwd *res,
|
||||
+ struct passwd **out);
|
||||
+
|
||||
#endif
|
||||
|
||||
/*
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
From 9d480426bfa2c68843ac8395b512e06fbdbcf53e Mon Sep 17 00:00:00 2001
|
||||
From: Roger Pau Monne <roger.pau@citrix.com>
|
||||
Date: Thu, 28 Sep 2023 12:22:35 +0200
|
||||
Subject: [PATCH 11/11] libxl: limit bootloader execution in restricted mode
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Introduce a timeout for bootloader execution when running in restricted mode.
|
||||
|
||||
Allow overwriting the default time out with an environment provided value.
|
||||
|
||||
This is part of XSA-443 / CVE-2023-34325
|
||||
|
||||
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
|
||||
---
|
||||
docs/man/xl.1.pod.in | 8 ++++++
|
||||
tools/libs/light/libxl_bootloader.c | 40 +++++++++++++++++++++++++++++
|
||||
tools/libs/light/libxl_internal.h | 2 ++
|
||||
3 files changed, 50 insertions(+)
|
||||
|
||||
diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in
|
||||
index 4831e122427d..c3eb6570ab8b 100644
|
||||
--- a/docs/man/xl.1.pod.in
|
||||
+++ b/docs/man/xl.1.pod.in
|
||||
@@ -1988,6 +1988,14 @@ NOTE: Each domain MUST have a SEPARATE username.
|
||||
|
||||
See docs/features/qemu-deprivilege.pandoc for more information.
|
||||
|
||||
+=item LIBXL_BOOTLOADER_TIMEOUT
|
||||
+
|
||||
+Timeout in seconds for bootloader execution when running in restricted mode.
|
||||
+Otherwise the build time default in LIBXL_BOOTLOADER_TIMEOUT will be used.
|
||||
+
|
||||
+If defined the value must be an unsigned integer between 0 and INT_MAX,
|
||||
+otherwise behavior is undefined. Setting to 0 disables the timeout.
|
||||
+
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c
|
||||
index 23c0ef3e8935..ee26d08f3765 100644
|
||||
--- a/tools/libs/light/libxl_bootloader.c
|
||||
+++ b/tools/libs/light/libxl_bootloader.c
|
||||
@@ -30,6 +30,8 @@ static void bootloader_keystrokes_copyfail(libxl__egc *egc,
|
||||
libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
|
||||
static void bootloader_display_copyfail(libxl__egc *egc,
|
||||
libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
|
||||
+static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev,
|
||||
+ const struct timeval *requested_abs, int rc);
|
||||
static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc,
|
||||
int rc);
|
||||
static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
|
||||
@@ -297,6 +299,7 @@ void libxl__bootloader_init(libxl__bootloader_state *bl)
|
||||
bl->ptys[0].master = bl->ptys[0].slave = 0;
|
||||
bl->ptys[1].master = bl->ptys[1].slave = 0;
|
||||
libxl__ev_child_init(&bl->child);
|
||||
+ libxl__ev_time_init(&bl->time);
|
||||
libxl__domaindeathcheck_init(&bl->deathcheck);
|
||||
bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes);
|
||||
bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display);
|
||||
@@ -314,6 +317,7 @@ static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl)
|
||||
libxl__domaindeathcheck_stop(gc,&bl->deathcheck);
|
||||
libxl__datacopier_kill(&bl->keystrokes);
|
||||
libxl__datacopier_kill(&bl->display);
|
||||
+ libxl__ev_time_deregister(gc, &bl->time);
|
||||
for (i=0; i<2; i++) {
|
||||
libxl__carefd_close(bl->ptys[i].master);
|
||||
libxl__carefd_close(bl->ptys[i].slave);
|
||||
@@ -375,6 +379,7 @@ static void bootloader_stop(libxl__egc *egc,
|
||||
|
||||
libxl__datacopier_kill(&bl->keystrokes);
|
||||
libxl__datacopier_kill(&bl->display);
|
||||
+ libxl__ev_time_deregister(gc, &bl->time);
|
||||
if (libxl__ev_child_inuse(&bl->child)) {
|
||||
r = kill(bl->child.pid, SIGTERM);
|
||||
if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]",
|
||||
@@ -637,6 +642,25 @@ static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op)
|
||||
|
||||
struct termios termattr;
|
||||
|
||||
+ if (getenv("LIBXL_BOOTLOADER_RESTRICT") ||
|
||||
+ getenv("LIBXL_BOOTLOADER_USER")) {
|
||||
+ const char *timeout_env = getenv("LIBXL_BOOTLOADER_TIMEOUT");
|
||||
+ int timeout = timeout_env ? atoi(timeout_env)
|
||||
+ : LIBXL_BOOTLOADER_TIMEOUT;
|
||||
+
|
||||
+ if (timeout) {
|
||||
+ /* Set execution timeout */
|
||||
+ rc = libxl__ev_time_register_rel(ao, &bl->time,
|
||||
+ bootloader_timeout,
|
||||
+ timeout * 1000);
|
||||
+ if (rc) {
|
||||
+ LOGED(ERROR, bl->domid,
|
||||
+ "unable to register timeout for bootloader execution");
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished);
|
||||
if (pid == -1) {
|
||||
rc = ERROR_FAIL;
|
||||
@@ -702,6 +726,21 @@ static void bootloader_display_copyfail(libxl__egc *egc,
|
||||
libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display);
|
||||
bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval);
|
||||
}
|
||||
+static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev,
|
||||
+ const struct timeval *requested_abs, int rc)
|
||||
+{
|
||||
+ libxl__bootloader_state *bl = CONTAINER_OF(ev, *bl, time);
|
||||
+ STATE_AO_GC(bl->ao);
|
||||
+
|
||||
+ libxl__ev_time_deregister(gc, &bl->time);
|
||||
+
|
||||
+ assert(libxl__ev_child_inuse(&bl->child));
|
||||
+ LOGD(ERROR, bl->domid, "killing bootloader because of timeout");
|
||||
+
|
||||
+ libxl__ev_child_kill_deregister(ao, &bl->child, SIGKILL);
|
||||
+
|
||||
+ bootloader_callback(egc, bl, rc);
|
||||
+}
|
||||
|
||||
static void bootloader_domaindeath(libxl__egc *egc,
|
||||
libxl__domaindeathcheck *dc,
|
||||
@@ -718,6 +757,7 @@ static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
|
||||
STATE_AO_GC(bl->ao);
|
||||
int rc;
|
||||
|
||||
+ libxl__ev_time_deregister(gc, &bl->time);
|
||||
libxl__datacopier_kill(&bl->keystrokes);
|
||||
libxl__datacopier_kill(&bl->display);
|
||||
|
||||
diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h
|
||||
index f1e3a9a15b13..d05783617ff5 100644
|
||||
--- a/tools/libs/light/libxl_internal.h
|
||||
+++ b/tools/libs/light/libxl_internal.h
|
||||
@@ -102,6 +102,7 @@
|
||||
#define LIBXL_QMP_CMD_TIMEOUT 10
|
||||
#define LIBXL_STUBDOM_START_TIMEOUT 30
|
||||
#define LIBXL_QEMU_BODGE_TIMEOUT 2
|
||||
+#define LIBXL_BOOTLOADER_TIMEOUT 120
|
||||
#define LIBXL_XENCONSOLE_LIMIT 1048576
|
||||
#define LIBXL_XENCONSOLE_PROTOCOL "vt100"
|
||||
#define LIBXL_MAXMEM_CONSTANT 1024
|
||||
@@ -3744,6 +3745,7 @@ struct libxl__bootloader_state {
|
||||
libxl__openpty_state openpty;
|
||||
libxl__openpty_result ptys[2]; /* [0] is for bootloader */
|
||||
libxl__ev_child child;
|
||||
+ libxl__ev_time time;
|
||||
libxl__domaindeathcheck deathcheck;
|
||||
int nargs, argsspace;
|
||||
const char **args;
|
||||
--
|
||||
2.42.0
|
||||
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Subject: x86/svm: Fix asymmetry with AMD DR MASK context switching
|
||||
|
||||
The handling of MSR_DR{0..3}_MASK is asymmetric between PV and HVM guests.
|
||||
|
||||
HVM guests context switch in based on the guest view of DBEXT, whereas PV
|
||||
guest switch in base on the host capability. Both guest types leave the
|
||||
context dirty for the next vCPU.
|
||||
|
||||
This leads to the following issue:
|
||||
|
||||
* PV or HVM guest has debugging active (%dr7 + mask)
|
||||
* Switch-out deactivates %dr7 but leaves other state stale in hardware
|
||||
* Another HVM guest with masks unavailable has debugging active
|
||||
* Switch in loads %dr7 but leaves the mask MSRs alone
|
||||
|
||||
Now, the second guest's vCPU is operating in the context of the prior vCPU's
|
||||
mask MSR, while the environment the vCPU can see says there are no mask MSRs.
|
||||
|
||||
As a stopgap, adjust the HVM path to switch in the masks based on host
|
||||
capabilities rather than guest visibility (i.e. like the PV path). Adjustment
|
||||
of the intercepts still needs to be dependent on the guest visibility of
|
||||
DBEXT.
|
||||
|
||||
This is part of XSA-444 / CVE-2023-34327
|
||||
|
||||
Fixes: c097f54912d3 ("x86/SVM: support data breakpoint extension registers")
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
|
||||
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
|
||||
index e8f50e7c5ec7..fd32600ae364 100644
|
||||
--- a/xen/arch/x86/hvm/svm/svm.c
|
||||
+++ b/xen/arch/x86/hvm/svm/svm.c
|
||||
@@ -339,6 +339,10 @@ static void svm_save_dr(struct vcpu *v)
|
||||
v->arch.hvm.flag_dr_dirty = 0;
|
||||
vmcb_set_dr_intercepts(vmcb, ~0u);
|
||||
|
||||
+ /*
|
||||
+ * The guest can only have changed the mask MSRs if we previous dropped
|
||||
+ * intercepts. Re-read them from hardware.
|
||||
+ */
|
||||
if ( v->domain->arch.cpuid->extd.dbext )
|
||||
{
|
||||
svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_RW);
|
||||
@@ -370,17 +374,25 @@ static void __restore_debug_registers(struct vmcb_struct *vmcb, struct vcpu *v)
|
||||
|
||||
ASSERT(v == current);
|
||||
|
||||
- if ( v->domain->arch.cpuid->extd.dbext )
|
||||
+ /*
|
||||
+ * Both the PV and HVM paths leave stale DR_MASK values in hardware on
|
||||
+ * context-switch-out. If we're activating %dr7 for the guest, we must
|
||||
+ * sync the DR_MASKs too, whether or not the guest can see them.
|
||||
+ */
|
||||
+ if ( boot_cpu_has(X86_FEATURE_DBEXT) )
|
||||
{
|
||||
- svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
- svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
- svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
- svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
-
|
||||
wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, v->arch.msrs->dr_mask[0]);
|
||||
wrmsrl(MSR_AMD64_DR1_ADDRESS_MASK, v->arch.msrs->dr_mask[1]);
|
||||
wrmsrl(MSR_AMD64_DR2_ADDRESS_MASK, v->arch.msrs->dr_mask[2]);
|
||||
wrmsrl(MSR_AMD64_DR3_ADDRESS_MASK, v->arch.msrs->dr_mask[3]);
|
||||
+
|
||||
+ if ( v->domain->arch.cpuid->extd.dbext )
|
||||
+ {
|
||||
+ svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
+ svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
+ svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
+ svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE);
|
||||
+ }
|
||||
}
|
||||
|
||||
write_debugreg(0, v->arch.dr[0]);
|
||||
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
|
||||
index e65cc6004148..06c4f3868b7a 100644
|
||||
--- a/xen/arch/x86/traps.c
|
||||
+++ b/xen/arch/x86/traps.c
|
||||
@@ -2281,6 +2281,11 @@ void activate_debugregs(const struct vcpu *curr)
|
||||
if ( curr->arch.dr7 & DR7_ACTIVE_MASK )
|
||||
write_debugreg(7, curr->arch.dr7);
|
||||
|
||||
+ /*
|
||||
+ * Both the PV and HVM paths leave stale DR_MASK values in hardware on
|
||||
+ * context-switch-out. If we're activating %dr7 for the guest, we must
|
||||
+ * sync the DR_MASKs too, whether or not the guest can see them.
|
||||
+ */
|
||||
if ( boot_cpu_has(X86_FEATURE_DBEXT) )
|
||||
{
|
||||
wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, curr->arch.msrs->dr_mask[0]);
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Subject: x86/pv: Correct the auditing of guest breakpoint addresses
|
||||
|
||||
The use of access_ok() is buggy, because it permits access to the compat
|
||||
translation area. 64bit PV guests don't use the XLAT area, but on AMD
|
||||
hardware, the DBEXT feature allows a breakpoint to match up to a 4G aligned
|
||||
region, allowing the breakpoint to reach outside of the XLAT area.
|
||||
|
||||
Prior to c/s cda16c1bb223 ("x86: mirror compat argument translation area for
|
||||
32-bit PV"), the live GDT was within 4G of the XLAT area.
|
||||
|
||||
All together, this allowed a malicious 64bit PV guest on AMD hardware to place
|
||||
a breakpoint over the live GDT, and trigger a #DB livelock (CVE-2015-8104).
|
||||
|
||||
Introduce breakpoint_addr_ok() and explain why __addr_ok() happens to be an
|
||||
appropriate check in this case.
|
||||
|
||||
For Xen 4.14 and later, this is a latent bug because the XLAT area has moved
|
||||
to be on its own with nothing interesting adjacent. For Xen 4.13 and older on
|
||||
AMD hardware, this fixes a PV-trigger-able DoS.
|
||||
|
||||
This is part of XSA-444 / CVE-2023-34328.
|
||||
|
||||
Fixes: 65e355490817 ("x86/PV: support data breakpoint extension registers")
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
|
||||
diff --git a/xen/arch/x86/include/asm/debugreg.h b/xen/arch/x86/include/asm/debugreg.h
|
||||
index c57914efc6e8..cc298265244b 100644
|
||||
--- a/xen/arch/x86/include/asm/debugreg.h
|
||||
+++ b/xen/arch/x86/include/asm/debugreg.h
|
||||
@@ -77,6 +77,26 @@
|
||||
asm volatile ( "mov %%db" #reg ",%0" : "=r" (__val) ); \
|
||||
__val; \
|
||||
})
|
||||
+
|
||||
+/*
|
||||
+ * Architecturally, %dr{0..3} can have any arbitrary value. However, Xen
|
||||
+ * can't allow the guest to breakpoint the Xen address range, so we limit the
|
||||
+ * guest to the lower canonical half, or above the Xen range in the higher
|
||||
+ * canonical half.
|
||||
+ *
|
||||
+ * Breakpoint lengths are specified to mask the low order address bits,
|
||||
+ * meaning all breakpoints are naturally aligned. With %dr7, the widest
|
||||
+ * breakpoint is 8 bytes. With DBEXT, the widest breakpoint is 4G. Both of
|
||||
+ * the Xen boundaries have >4G alignment.
|
||||
+ *
|
||||
+ * In principle we should account for HYPERVISOR_COMPAT_VIRT_START(d), but
|
||||
+ * 64bit Xen has never enforced this for compat guests, and there's no problem
|
||||
+ * (to Xen) if the guest breakpoints it's alias of the M2P. Skipping this
|
||||
+ * aspect simplifies the logic, and causes us not to reject a migrating guest
|
||||
+ * which operated fine on prior versions of Xen.
|
||||
+ */
|
||||
+#define breakpoint_addr_ok(a) __addr_ok(a)
|
||||
+
|
||||
long set_debugreg(struct vcpu *, unsigned int reg, unsigned long value);
|
||||
void activate_debugregs(const struct vcpu *);
|
||||
|
||||
diff --git a/xen/arch/x86/pv/misc-hypercalls.c b/xen/arch/x86/pv/misc-hypercalls.c
|
||||
index aaaf70eb6330..f8636de907ae 100644
|
||||
--- a/xen/arch/x86/pv/misc-hypercalls.c
|
||||
+++ b/xen/arch/x86/pv/misc-hypercalls.c
|
||||
@@ -72,7 +72,7 @@ long set_debugreg(struct vcpu *v, unsigned int reg, unsigned long value)
|
||||
switch ( reg )
|
||||
{
|
||||
case 0 ... 3:
|
||||
- if ( !access_ok(value, sizeof(long)) )
|
||||
+ if ( !breakpoint_addr_ok(value) )
|
||||
return -EPERM;
|
||||
|
||||
v->arch.dr[reg] = value;
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
From a43127d4f1f9a364334fe16b6239c211b35fd238 Mon Sep 17 00:00:00 2001
|
||||
From: Roger Pau Monne <roger.pau@citrix.com>
|
||||
Date: Wed, 11 Oct 2023 13:14:21 +0200
|
||||
Subject: [PATCH] iommu/amd-vi: use correct level for quarantine domain page
|
||||
tables
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The current setup of the quarantine page tables assumes that the quarantine
|
||||
domain (dom_io) has been initialized with an address width of
|
||||
DEFAULT_DOMAIN_ADDRESS_WIDTH (48).
|
||||
|
||||
However dom_io being a PV domain gets the AMD-Vi IOMMU page tables levels based
|
||||
on the maximum (hot pluggable) RAM address, and hence on systems with no RAM
|
||||
above the 512GB mark only 3 page-table levels are configured in the IOMMU.
|
||||
|
||||
On systems without RAM above the 512GB boundary amd_iommu_quarantine_init()
|
||||
will setup page tables for the scratch page with 4 levels, while the IOMMU will
|
||||
be configured to use 3 levels only. The page destined to be used as level 1,
|
||||
and to contain a directory of PTEs ends up being the address in a PTE itself,
|
||||
and thus level 1 page becomes the leaf page. Without the level mismatch it's
|
||||
level 0 page that should be the leaf page instead.
|
||||
|
||||
The level 1 page won't be used as such, and hence it's not possible to use it
|
||||
to gain access to other memory on the system. However that page is not cleared
|
||||
in amd_iommu_quarantine_init() as part of re-initialization of the device
|
||||
quarantine page tables, and hence data on the level 1 page can be leaked
|
||||
between device usages.
|
||||
|
||||
Fix this by making sure the paging levels setup by amd_iommu_quarantine_init()
|
||||
match the number configured on the IOMMUs.
|
||||
|
||||
Note that IVMD regions are not affected by this issue, as those areas are
|
||||
mapped taking the configured paging levels into account.
|
||||
|
||||
This is XSA-445 / CVE-2023-46835
|
||||
|
||||
Fixes: ea38867831da ('x86 / iommu: set up a scratch page in the quarantine domain')
|
||||
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
Reviewed-by: Jan Beulich <jbeulich@suse.com>
|
||||
---
|
||||
xen/drivers/passthrough/amd/iommu_map.c | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c
|
||||
index 993bac6f8878..e0f4fe736a8d 100644
|
||||
--- a/xen/drivers/passthrough/amd/iommu_map.c
|
||||
+++ b/xen/drivers/passthrough/amd/iommu_map.c
|
||||
@@ -837,9 +837,7 @@ static int fill_qpt(union amd_iommu_pte *this, unsigned int level,
|
||||
int cf_check amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page)
|
||||
{
|
||||
struct domain_iommu *hd = dom_iommu(dom_io);
|
||||
- unsigned long end_gfn =
|
||||
- 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT);
|
||||
- unsigned int level = amd_iommu_get_paging_mode(end_gfn);
|
||||
+ unsigned int level = hd->arch.amd.paging_mode;
|
||||
unsigned int req_id = get_dma_requestor_id(pdev->seg, pdev->sbdf.bdf);
|
||||
const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg);
|
||||
int rc;
|
||||
--
|
||||
2.42.0
|
||||
|
||||
115
xsa446.patch
115
xsa446.patch
|
|
@ -1,115 +0,0 @@
|
|||
From 80d5aada598c3a800a350003d5d582931545e13c Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Date: Thu, 26 Oct 2023 14:37:38 +0100
|
||||
Subject: [PATCH] x86/spec-ctrl: Remove conditional IRQs-on-ness for INT
|
||||
$0x80/0x82 paths
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Before speculation defences, some paths in Xen could genuinely get away with
|
||||
being IRQs-on at entry. But XPTI invalidated this property on most paths, and
|
||||
attempting to maintain it on the remaining paths was a mistake.
|
||||
|
||||
Fast forward, and DO_SPEC_CTRL_COND_IBPB (protection for AMD BTC/SRSO) is not
|
||||
IRQ-safe, running with IRQs enabled in some cases. The other actions taken on
|
||||
these paths happen to be IRQ-safe.
|
||||
|
||||
Make entry_int82() and int80_direct_trap() unconditionally Interrupt Gates
|
||||
rather than Trap Gates. Remove the conditional re-adjustment of
|
||||
int80_direct_trap() in smp_prepare_cpus(), and have entry_int82() explicitly
|
||||
enable interrupts when safe to do so.
|
||||
|
||||
In smp_prepare_cpus(), with the conditional re-adjustment removed, the
|
||||
clearing of pv_cr3 is the only remaining action gated on XPTI, and it is out
|
||||
of place anyway, repeating work already done by smp_prepare_boot_cpu(). Drop
|
||||
the entire if() condition to avoid leaving an incorrect vestigial remnant.
|
||||
|
||||
Also drop comments which make incorrect statements about when its safe to
|
||||
enable interrupts.
|
||||
|
||||
This is XSA-446 / CVE-2023-46836
|
||||
|
||||
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||||
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
|
||||
---
|
||||
xen/arch/x86/pv/traps.c | 4 ++--
|
||||
xen/arch/x86/smpboot.c | 14 --------------
|
||||
xen/arch/x86/x86_64/compat/entry.S | 2 ++
|
||||
xen/arch/x86/x86_64/entry.S | 1 -
|
||||
4 files changed, 4 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/xen/arch/x86/pv/traps.c b/xen/arch/x86/pv/traps.c
|
||||
index 74f333da7e1c..240d1a2db7a3 100644
|
||||
--- a/xen/arch/x86/pv/traps.c
|
||||
+++ b/xen/arch/x86/pv/traps.c
|
||||
@@ -139,11 +139,11 @@ void __init pv_trap_init(void)
|
||||
#ifdef CONFIG_PV32
|
||||
/* The 32-on-64 hypercall vector is only accessible from ring 1. */
|
||||
_set_gate(idt_table + HYPERCALL_VECTOR,
|
||||
- SYS_DESC_trap_gate, 1, entry_int82);
|
||||
+ SYS_DESC_irq_gate, 1, entry_int82);
|
||||
#endif
|
||||
|
||||
/* Fast trap for int80 (faster than taking the #GP-fixup path). */
|
||||
- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_trap_gate, 3,
|
||||
+ _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3,
|
||||
&int80_direct_trap);
|
||||
|
||||
open_softirq(NMI_SOFTIRQ, nmi_softirq);
|
||||
diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c
|
||||
index 3a1a659082c6..4c54ecbc91d7 100644
|
||||
--- a/xen/arch/x86/smpboot.c
|
||||
+++ b/xen/arch/x86/smpboot.c
|
||||
@@ -1158,20 +1158,6 @@ void __init smp_prepare_cpus(void)
|
||||
|
||||
stack_base[0] = (void *)((unsigned long)stack_start & ~(STACK_SIZE - 1));
|
||||
|
||||
- if ( opt_xpti_hwdom || opt_xpti_domu )
|
||||
- {
|
||||
- get_cpu_info()->pv_cr3 = 0;
|
||||
-
|
||||
-#ifdef CONFIG_PV
|
||||
- /*
|
||||
- * All entry points which may need to switch page tables have to start
|
||||
- * with interrupts off. Re-write what pv_trap_init() has put there.
|
||||
- */
|
||||
- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3,
|
||||
- &int80_direct_trap);
|
||||
-#endif
|
||||
- }
|
||||
-
|
||||
set_nr_sockets();
|
||||
|
||||
socket_cpumask = xzalloc_array(cpumask_t *, nr_sockets);
|
||||
diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S
|
||||
index bd5abd8040bd..fcc3a721f147 100644
|
||||
--- a/xen/arch/x86/x86_64/compat/entry.S
|
||||
+++ b/xen/arch/x86/x86_64/compat/entry.S
|
||||
@@ -21,6 +21,8 @@ ENTRY(entry_int82)
|
||||
SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */
|
||||
/* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */
|
||||
|
||||
+ sti
|
||||
+
|
||||
CR4_PV32_RESTORE
|
||||
|
||||
GET_CURRENT(bx)
|
||||
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
|
||||
index 5ca74f5f62b2..9a7b129aa7e4 100644
|
||||
--- a/xen/arch/x86/x86_64/entry.S
|
||||
+++ b/xen/arch/x86/x86_64/entry.S
|
||||
@@ -327,7 +327,6 @@ ENTRY(sysenter_entry)
|
||||
#ifdef CONFIG_XEN_SHSTK
|
||||
ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK
|
||||
#endif
|
||||
- /* sti could live here when we don't switch page tables below. */
|
||||
pushq $FLAT_USER_SS
|
||||
pushq $0
|
||||
pushfq
|
||||
|
||||
base-commit: 7befef87cc9b1bb8ca15d866ce1ecd9165ccb58c
|
||||
prerequisite-patch-id: 142a87c707411d49e136c3fb76f1b14963ec6dc8
|
||||
--
|
||||
2.30.2
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue