Compare commits

...
Sign in to create a new pull request.

9 commits

Author SHA1 Message Date
Michael Young
e56f0088cf 4 security patches
oxenstored keeps quota related use counts across domain destruction
        [XSA-483, CVE-2026-23556]
Xenstored DoS via XS_RESET_WATCHES command [XSA-484, CVE-2026-23557]
grant table v2 race in status page mapping [XSA-486, CVE-2026-23558]
x86: Floating Point Divider State Sampling [XSA-488, CVE-2025-54505]
2026-04-29 21:10:04 +01:00
Michael Young
6a77b77734 update to xen 4.19.5 2026-03-27 20:56:28 +00:00
Michael Young
f9e6579959 2 security updates
Use after free of paging structures in EPT [XSA-480, CVE-2026-23554]
Xenstored DoS by unprivileged domain [XSA-481, CVE-2026-23555]
2026-03-18 19:47:25 +00:00
Michael Young
aad83ce331 2 security updates
x86: buffer overrun with shadow paging + tracing [XSA-477, CVE-2025-58150]
        (#2434045)
x86: incomplete IBPB for vCPU isolation [XSA-479, CVE-2026-23553]
        (#2434047)
2026-01-29 20:26:25 +00:00
Michael Young
1a1a856cfd update to xen-4.19.4 2025-12-06 11:58:21 +00:00
Michael Young
ddf24e79d8 Incorrect removal of permissions on PCI device unplug 2025-10-24 21:39:59 +01:00
Michael Young
57b04934fa x86: Incorrect input sanitisation in Viridian hypercalls
[XSA-475, CVE-2025-58147, CVE-2025-58148]
2025-10-21 22:12:58 +01:00
Michael Young
f83ebf51c1 2 security updates
Mutiple vulnerabilities in the Viridian interface [XSA-472,
            CVE-2025-27466, CVE-2025-58142, CVE-2025-58143]
Arm issues with page refcounting [XSA-473, CVE-2025-58144, CVE-2025-58145]
2025-09-15 11:15:04 +01:00
Michael Young
f8d96e12e3 update to xen-4.19.3
includes patches for
x86: Incorrect stubs exception handling for flags recovery [XSA-470,
        CVE-2025-27465]
x86: Transitive Scheduler Attacks [XSA-471, CVE-2024-36350,
        CVE-2024-36357]
2025-08-05 21:43:19 +01:00
14 changed files with 444 additions and 1556 deletions

2
.gitignore vendored
View file

@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz
pciutils-2.2.9.tar.bz2
zlib-1.2.3.tar.gz
polarssl-1.1.4-gpl.tgz
/xen-4.19.2.tar.gz
/xen-4.19.5.tar.gz

View file

@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77
SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e
SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad
SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5
SHA512 (xen-4.19.2.tar.gz) = 35147b0d755b5a603915fa5017bc021d4a238ab96ee1139dfee8e0f6ba76e64b89d5f331fbd60549682f45ee983d3d8f1cb95c53eed22373a7fa06074ca8da09
SHA512 (xen-4.19.5.tar.gz) = 7472a0fcf159b12ac464bba867bea8852de991f136fd5ca5bf20ae8826ef8659a58a5437eeb79800375b7e4be59d3f6c92b2a73bb2d73fcfbf8dea73a5d303dd

View file

@ -0,0 +1,88 @@
From 60a2c34900373ded0c81dbb7367792178b6c33b9 Mon Sep 17 00:00:00 2001
From: Andrew Cooper <andrew.cooper3@citrix.com>
Date: Fri, 10 Apr 2026 21:55:46 +0100
Subject: [PATCH] x86/amd: Mitigate AMD-SN-7053 / FP-DSS
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit
This is XSA-488 / CVE-2025-54505
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
(cherry picked from commit 99912d346009fda1e7fb1510c9501fbab17e92a0)
---
xen/arch/x86/cpu/amd.c | 37 ++++++++++++++++++++++++++++
xen/arch/x86/include/asm/msr-index.h | 1 +
2 files changed, 38 insertions(+)
diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c
index ebeed00941..12876de1df 100644
--- a/xen/arch/x86/cpu/amd.c
+++ b/xen/arch/x86/cpu/amd.c
@@ -982,6 +982,42 @@ void amd_init_de_cfg(const struct cpuinfo_x86 *c)
wrmsrl(MSR_AMD64_DE_CFG, val | new);
}
+static void amd_init_fp_cfg(const struct cpuinfo_x86 *c)
+{
+ uint64_t val, new = 0;
+
+ /* If virtualised, we won't have mutable access even if we can read it. */
+ if ( cpu_has_hypervisor )
+ return;
+
+ /*
+ * On Zen1, mitigate SB-7053 / FP-DSS Floating Point Divider State
+ * Sampling by setting bit 9 as instructed.
+ */
+ if ( c->x86 == 0x17 && is_zen1_uarch() )
+ new |= 1 << 9;
+
+ /*
+ * Avoid reading FP_CFG if we don't intend to change anything. The
+ * register doesn't exist on all families.
+ */
+ if ( !new )
+ return;
+
+ rdmsrl(MSR_AMD64_FP_CFG, val);
+
+ if ( (val & new) == new )
+ return;
+
+ /*
+ * FP_CFG is a Core-scoped MSR, and this write is racy. However, both
+ * threads calculate the new value from state which expected to be
+ * consistent across CPUs and unrelated to the old value, so the result
+ * should be consistent.
+ */
+ wrmsrl(MSR_AMD64_FP_CFG, val | new);
+}
+
void __init amd_init_lfence_dispatch(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
@@ -1055,6 +1091,7 @@ static void cf_check init_amd(struct cpuinfo_x86 *c)
unsigned long long value;
amd_init_de_cfg(c);
+ amd_init_fp_cfg(c);
if (c == &boot_cpu_data)
amd_init_lfence_dispatch(); /* Needs amd_init_de_cfg() */
diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h
index 6f2c3147e3..70e5f09a2d 100644
--- a/xen/arch/x86/include/asm/msr-index.h
+++ b/xen/arch/x86/include/asm/msr-index.h
@@ -411,6 +411,7 @@
#define MSR_AMD64_LS_CFG 0xc0011020U
#define MSR_AMD64_IC_CFG 0xc0011021U
#define MSR_AMD64_DC_CFG 0xc0011022U
+#define MSR_AMD64_FP_CFG 0xc0011028U
#define MSR_AMD64_DE_CFG 0xc0011029U
#define AMD64_DE_CFG_LFENCE_SERIALISE (_AC(1, ULL) << 1)
#define MSR_AMD64_EX_CFG 0xc001102cU
--
2.39.5

View file

@ -54,8 +54,8 @@
Summary: Xen is a virtual machine monitor
Name: xen
Version: 4.19.2
Release: 4%{?dist}
Version: 4.19.5
Release: 2%{?dist}
# Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended.
License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD
URL: http://xen.org/
@ -111,13 +111,10 @@ Patch43: xen.gcc11.fixes.patch
Patch45: xen.gcc12.fixes.patch
Patch46: xen.efi.build.patch
Patch49: xen.python3.12.patch
Patch50: xsa469-4.19-01.patch
Patch51: xsa469-4.19-02.patch
Patch52: xsa469-4.19-03.patch
Patch53: xsa469-4.19-04.patch
Patch54: xsa469-4.19-05.patch
Patch55: xsa469-4.19-06.patch
Patch56: xsa469-4.19-07.patch
Patch50: xsa483.patch
Patch51: xsa484.patch
Patch52: xsa486.patch
Patch53: xen.git-60a2c34900373ded0c81dbb7367792178b6c33b9.patch
%if %build_qemutrad
@ -330,9 +327,6 @@ manage Xen virtual machines.
%patch 51 -p1
%patch 52 -p1
%patch 53 -p1
%patch 54 -p1
%patch 55 -p1
%patch 56 -p1
# qemu-xen-traditional patches
pushd tools/qemu-xen-traditional
@ -951,6 +945,54 @@ fi
%endif
%changelog
* Wed Apr 29 2026 Michael Young <m.a.young@durham.ac.uk> - 4.19.5-2
- oxenstored keeps quota related use counts across domain destruction
[XSA-483, CVE-2026-23556]
- Xenstored DoS via XS_RESET_WATCHES command [XSA-484, CVE-2026-23557]
- grant table v2 race in status page mapping [XSA-486, CVE-2026-23558]
- x86: Floating Point Divider State Sampling [XSA-488, CVE-2025-54505]
* Fri Mar 27 2026 Michael Young <m.a.young@durham.ac.uk> - 4.19.5-1
- update to xen 4.19.5
remove patches now included or superceded upstream
* Wed Mar 18 2026 Michael Young <m.a.young@durham.ac.uk> - 4.19.4-3
- Use after free of paging structures in EPT [XSA-480, CVE-2026-23554]
- Xenstored DoS by unprivileged domain [XSA-481, CVE-2026-23555]
* Thu Jan 29 2026 Michael Young <m.a.young@durham.ac.uk> - 4.19.4-2
x86: buffer overrun with shadow paging + tracing [XSA-477, CVE-2025-58150]
(#2434045)
x86: incomplete IBPB for vCPU isolation [XSA-479, CVE-2026-23553]
(#2434047)
* Sat Dec 06 2025 Michael Young <m.a.young@durham.ac.uk> - 4.19.4-1
- update to xen-4.19.4
remove patches now included or superceded upstream
* Fri Oct 24 2025 Michael Young <m.a.young@durham.ac.uk> - 4.19.3-8
- Incorrect removal of permissions on PCI device unplug [XSA-476,
CVE-2025-58149]
* Tue Oct 21 2025 Michael Young <m.a.young@durham.ac.uk> - 4.19.3-6
- x86: Incorrect input sanitisation in Viridian hypercalls [XSA-475,
CVE-2025-58147, CVE-2025-58148]
* Mon Sep 15 2025 Michael Young <m.a.young@durham.ac.uk> - 4.19.3-4
- Mutiple vulnerabilities in the Viridian interface [XSA-472,
CVE-2025-27466, CVE-2025-58142, CVE-2025-58143]
- Arm issues with page refcounting [XSA-473, CVE-2025-58144,
CVE-2025-58145]
* Tue Aug 05 2025 Michael Young <m.a.young@durham.ac.uk> - 4.19.3-2
- update to xen-4.19.3
remove patches now included or superceded upstream
includes patches for
x86: Incorrect stubs exception handling for flags recovery [XSA-470,
CVE-2025-27465]
x86: Transitive Scheduler Attacks [XSA-471, CVE-2024-36350,
CVE-2024-36357]
* Mon May 12 2025 Michael Young <m.a.young@durham.ac.uk> - 4.19.2-4
- x86: Indirect Target Selection [XSA-469, CVE-2024-28956]

View file

@ -1,83 +0,0 @@
From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: x86/alternative: Support replacements when a feature is not present
Use the top bit of a->cpuid to express inverted polarity. This requires
stripping the top bit back out when performing the sanity checks.
Despite only being used once, create a replace boolean to express the decision
more clearly in _apply_alternatives().
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index 1ba35cb9ede9..88c90044c20d 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -197,6 +197,8 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start,
uint8_t *repl = ALT_REPL_PTR(a);
uint8_t buf[MAX_PATCH_LEN];
unsigned int total_len = a->orig_len + a->pad_len;
+ unsigned int feat = a->cpuid & ~ALT_FLAG_NOT;
+ bool inv = a->cpuid & ALT_FLAG_NOT, replace;
if ( a->repl_len > total_len )
{
@@ -214,11 +216,11 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start,
return -ENOSPC;
}
- if ( a->cpuid >= NCAPINTS * 32 )
+ if ( feat >= NCAPINTS * 32 )
{
printk(XENLOG_ERR
"Alt for %ps, feature %#x outside of featureset range %#x\n",
- ALT_ORIG_PTR(a), a->cpuid, NCAPINTS * 32);
+ ALT_ORIG_PTR(a), feat, NCAPINTS * 32);
return -ERANGE;
}
@@ -243,8 +245,14 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start,
continue;
}
+ /*
+ * Should a replacement be performed? Most replacements have positive
+ * polarity, but we support negative polarity too.
+ */
+ replace = boot_cpu_has(feat) ^ inv;
+
/* If there is no replacement to make, see about optimising the nops. */
- if ( !boot_cpu_has(a->cpuid) )
+ if ( !replace )
{
/* Origin site site already touched? Don't nop anything. */
if ( base->priv )
diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h
index 69555d781ef9..89b7bdcb82e5 100644
--- a/xen/arch/x86/include/asm/alternative.h
+++ b/xen/arch/x86/include/asm/alternative.h
@@ -1,6 +1,13 @@
#ifndef __X86_ALTERNATIVE_H__
#define __X86_ALTERNATIVE_H__
+/*
+ * Common to both C and ASM. Express a replacement when a feature is not
+ * available.
+ */
+#define ALT_FLAG_NOT (1 << 15)
+#define ALT_NOT(x) (ALT_FLAG_NOT | (x))
+
#ifdef __ASSEMBLY__
#include <asm/alternative-asm.h>
#else
@@ -11,7 +18,7 @@
struct __packed alt_instr {
int32_t orig_offset; /* original instruction */
int32_t repl_offset; /* offset to replacement instruction */
- uint16_t cpuid; /* cpuid bit set for replacement */
+ uint16_t cpuid; /* cpuid bit set for replacement (top bit is polarity) */
uint8_t orig_len; /* length of original instruction */
uint8_t repl_len; /* length of new instruction */
uint8_t pad_len; /* length of build-time padding */

View file

@ -1,314 +0,0 @@
From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: x86/guest: Remove use of the Xen hypercall_page
In order to protect against ITS, Xen needs to start using return thunks.
Therefore the advice in XSA-466 becomes relevant, and the hypercall_page needs
to be removed.
Implement early_hypercall(), with infrastructure to figure out the correct
instruction on first use. Use ALTERNATIVE()s to result in inline hypercalls,
including the ALT_NOT() form so we only need a single synthetic feature bit.
No overall change.
This is part of XSA-469 / CVE-2024-28956
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
diff --git a/xen/arch/x86/guest/xen/Makefile b/xen/arch/x86/guest/xen/Makefile
index 26fb4b1007c0..8b3250aa8886 100644
--- a/xen/arch/x86/guest/xen/Makefile
+++ b/xen/arch/x86/guest/xen/Makefile
@@ -1,4 +1,4 @@
-obj-y += hypercall_page.o
+obj-bin-y += hypercall.init.o
obj-y += xen.o
obj-bin-$(CONFIG_PVH_GUEST) += pvh-boot.init.o
diff --git a/xen/arch/x86/guest/xen/hypercall.S b/xen/arch/x86/guest/xen/hypercall.S
new file mode 100644
index 000000000000..05e429794cc4
--- /dev/null
+++ b/xen/arch/x86/guest/xen/hypercall.S
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <xen/linkage.h>
+
+ .section .init.text, "ax", @progbits
+
+ /*
+ * Used during early boot, before alternatives have run and inlined
+ * the appropriate instruction. Called using the hypercall ABI.
+ */
+FUNC(early_hypercall)
+ cmpb $0, early_hypercall_insn(%rip)
+ jl .L_setup
+ je 1f
+
+ vmmcall
+ ret
+
+1: vmcall
+ ret
+
+.L_setup:
+ /*
+ * When setting up the first time around, all registers need
+ * preserving. Save the non-callee-saved ones.
+ */
+ push %r11
+ push %r10
+ push %r9
+ push %r8
+ push %rdi
+ push %rsi
+ push %rdx
+ push %rcx
+ push %rax
+
+ call early_hypercall_setup
+
+ pop %rax
+ pop %rcx
+ pop %rdx
+ pop %rsi
+ pop %rdi
+ pop %r8
+ pop %r9
+ pop %r10
+ pop %r11
+
+ jmp early_hypercall
+END(early_hypercall)
diff --git a/xen/arch/x86/guest/xen/hypercall_page.S b/xen/arch/x86/guest/xen/hypercall_page.S
deleted file mode 100644
index 7ab55fc1f6e6..000000000000
--- a/xen/arch/x86/guest/xen/hypercall_page.S
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <asm/page.h>
-#include <asm/asm_defns.h>
-#include <public/xen.h>
-
- .section ".text.page_aligned", "ax", @progbits
-
-DATA(hypercall_page, PAGE_SIZE)
- /* Poisoned with `ret` for safety before hypercalls are set up. */
- .fill PAGE_SIZE, 1, 0xc3
-END(hypercall_page)
-
-/*
- * Identify a specific hypercall in the hypercall page
- * @param name Hypercall name.
- */
-#define DECLARE_HYPERCALL(name) \
- .globl HYPERCALL_ ## name; \
- .type HYPERCALL_ ## name, STT_FUNC; \
- .size HYPERCALL_ ## name, 32; \
- .set HYPERCALL_ ## name, hypercall_page + __HYPERVISOR_ ## name * 32
-
-DECLARE_HYPERCALL(set_trap_table)
-DECLARE_HYPERCALL(mmu_update)
-DECLARE_HYPERCALL(set_gdt)
-DECLARE_HYPERCALL(stack_switch)
-DECLARE_HYPERCALL(set_callbacks)
-DECLARE_HYPERCALL(fpu_taskswitch)
-DECLARE_HYPERCALL(sched_op_compat)
-DECLARE_HYPERCALL(platform_op)
-DECLARE_HYPERCALL(set_debugreg)
-DECLARE_HYPERCALL(get_debugreg)
-DECLARE_HYPERCALL(update_descriptor)
-DECLARE_HYPERCALL(memory_op)
-DECLARE_HYPERCALL(multicall)
-DECLARE_HYPERCALL(update_va_mapping)
-DECLARE_HYPERCALL(set_timer_op)
-DECLARE_HYPERCALL(event_channel_op_compat)
-DECLARE_HYPERCALL(xen_version)
-DECLARE_HYPERCALL(console_io)
-DECLARE_HYPERCALL(physdev_op_compat)
-DECLARE_HYPERCALL(grant_table_op)
-DECLARE_HYPERCALL(vm_assist)
-DECLARE_HYPERCALL(update_va_mapping_otherdomain)
-DECLARE_HYPERCALL(iret)
-DECLARE_HYPERCALL(vcpu_op)
-DECLARE_HYPERCALL(set_segment_base)
-DECLARE_HYPERCALL(mmuext_op)
-DECLARE_HYPERCALL(xsm_op)
-DECLARE_HYPERCALL(nmi_op)
-DECLARE_HYPERCALL(sched_op)
-DECLARE_HYPERCALL(callback_op)
-DECLARE_HYPERCALL(xenoprof_op)
-DECLARE_HYPERCALL(event_channel_op)
-DECLARE_HYPERCALL(physdev_op)
-DECLARE_HYPERCALL(hvm_op)
-DECLARE_HYPERCALL(sysctl)
-DECLARE_HYPERCALL(domctl)
-DECLARE_HYPERCALL(kexec_op)
-DECLARE_HYPERCALL(argo_op)
-DECLARE_HYPERCALL(xenpmu_op)
-
-DECLARE_HYPERCALL(arch_0)
-DECLARE_HYPERCALL(arch_1)
-DECLARE_HYPERCALL(arch_2)
-DECLARE_HYPERCALL(arch_3)
-DECLARE_HYPERCALL(arch_4)
-DECLARE_HYPERCALL(arch_5)
-DECLARE_HYPERCALL(arch_6)
-DECLARE_HYPERCALL(arch_7)
-
-/*
- * Local variables:
- * tab-width: 8
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/xen/arch/x86/guest/xen/xen.c b/xen/arch/x86/guest/xen/xen.c
index 7484b3f73ad3..2c30db05dfa7 100644
--- a/xen/arch/x86/guest/xen/xen.c
+++ b/xen/arch/x86/guest/xen/xen.c
@@ -26,7 +26,6 @@
bool __read_mostly xen_guest;
uint32_t __read_mostly xen_cpuid_base;
-extern char hypercall_page[];
static struct rangeset *mem;
DEFINE_PER_CPU(unsigned int, vcpu_id);
@@ -35,6 +34,50 @@ static struct vcpu_info *vcpu_info;
static unsigned long vcpu_info_mapped[BITS_TO_LONGS(NR_CPUS)];
DEFINE_PER_CPU(struct vcpu_info *, vcpu_info);
+/*
+ * Which instruction to use for early hypercalls:
+ * < 0 setup
+ * 0 vmcall
+ * > 0 vmmcall
+ */
+int8_t __initdata early_hypercall_insn = -1;
+
+/*
+ * Called once during the first hypercall to figure out which instruction to
+ * use. Error handling options are limited.
+ */
+void asmlinkage __init early_hypercall_setup(void)
+{
+ BUG_ON(early_hypercall_insn != -1);
+
+ if ( !boot_cpu_data.x86_vendor )
+ {
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(0, &eax, &ebx, &ecx, &edx);
+
+ boot_cpu_data.x86_vendor = x86_cpuid_lookup_vendor(ebx, ecx, edx);
+ }
+
+ switch ( boot_cpu_data.x86_vendor )
+ {
+ case X86_VENDOR_INTEL:
+ case X86_VENDOR_CENTAUR:
+ case X86_VENDOR_SHANGHAI:
+ early_hypercall_insn = 0;
+ setup_force_cpu_cap(X86_FEATURE_USE_VMCALL);
+ break;
+
+ case X86_VENDOR_AMD:
+ case X86_VENDOR_HYGON:
+ early_hypercall_insn = 1;
+ break;
+
+ default:
+ BUG();
+ }
+}
+
static void __init find_xen_leaves(void)
{
uint32_t eax, ebx, ecx, edx, base;
@@ -337,9 +380,6 @@ const struct hypervisor_ops *__init xg_probe(void)
if ( !xen_cpuid_base )
return NULL;
- /* Fill the hypercall page. */
- wrmsrl(cpuid_ebx(xen_cpuid_base + 2), __pa(hypercall_page));
-
xen_guest = true;
return &ops;
diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h
index ba3df174b76e..9e3ed21c026d 100644
--- a/xen/arch/x86/include/asm/cpufeatures.h
+++ b/xen/arch/x86/include/asm/cpufeatures.h
@@ -42,6 +42,7 @@ XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks *
XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */
XEN_CPUFEATURE(IBPB_ENTRY_PV, X86_SYNTH(28)) /* MSR_PRED_CMD used by Xen for PV */
XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for HVM */
+XEN_CPUFEATURE(USE_VMCALL, X86_SYNTH(30)) /* Use VMCALL instead of VMMCALL */
/* Bug words follow the synthetic words. */
#define X86_NR_BUG 1
diff --git a/xen/arch/x86/include/asm/guest/xen-hcall.h b/xen/arch/x86/include/asm/guest/xen-hcall.h
index 665b472d05ac..96004dec9909 100644
--- a/xen/arch/x86/include/asm/guest/xen-hcall.h
+++ b/xen/arch/x86/include/asm/guest/xen-hcall.h
@@ -30,9 +30,11 @@
({ \
long res, tmp__; \
asm volatile ( \
- "call hypercall_page + %c[offset]" \
+ ALTERNATIVE_2("call early_hypercall", \
+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \
+ "vmcall", X86_FEATURE_USE_VMCALL) \
: "=a" (res), "=D" (tmp__) ASM_CALL_CONSTRAINT \
- : [offset] "i" (hcall * 32), \
+ : "0" (hcall), \
"1" ((long)(a1)) \
: "memory" ); \
(type)res; \
@@ -42,10 +44,12 @@
({ \
long res, tmp__; \
asm volatile ( \
- "call hypercall_page + %c[offset]" \
+ ALTERNATIVE_2("call early_hypercall", \
+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \
+ "vmcall", X86_FEATURE_USE_VMCALL) \
: "=a" (res), "=D" (tmp__), "=S" (tmp__) \
ASM_CALL_CONSTRAINT \
- : [offset] "i" (hcall * 32), \
+ : "0" (hcall), \
"1" ((long)(a1)), "2" ((long)(a2)) \
: "memory" ); \
(type)res; \
@@ -55,10 +59,12 @@
({ \
long res, tmp__; \
asm volatile ( \
- "call hypercall_page + %c[offset]" \
+ ALTERNATIVE_2("call early_hypercall", \
+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \
+ "vmcall", X86_FEATURE_USE_VMCALL) \
: "=a" (res), "=D" (tmp__), "=S" (tmp__), "=d" (tmp__) \
ASM_CALL_CONSTRAINT \
- : [offset] "i" (hcall * 32), \
+ : "0" (hcall), \
"1" ((long)(a1)), "2" ((long)(a2)), "3" ((long)(a3)) \
: "memory" ); \
(type)res; \
@@ -69,10 +75,12 @@
long res, tmp__; \
register long _a4 asm ("r10") = ((long)(a4)); \
asm volatile ( \
- "call hypercall_page + %c[offset]" \
+ ALTERNATIVE_2("call early_hypercall", \
+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \
+ "vmcall", X86_FEATURE_USE_VMCALL) \
: "=a" (res), "=D" (tmp__), "=S" (tmp__), "=d" (tmp__), \
"=&r" (tmp__) ASM_CALL_CONSTRAINT \
- : [offset] "i" (hcall * 32), \
+ : "0" (hcall), \
"1" ((long)(a1)), "2" ((long)(a2)), "3" ((long)(a3)), \
"4" (_a4) \
: "memory" ); \

View file

@ -1,46 +0,0 @@
From: Jan Beulich <jbeulich@suse.com>
Subject: x86/thunk: (Mis)align __x86_indirect_thunk_* to mitigate ITS
The Indirect Target Selection speculative vulnerability means that indirect
branches (including RETs) are unsafe when in the first half of a cacheline.
Arrange for __x86_indirect_thunk_* to always be in the second half.
This is part of XSA-469 / CVE-2024-28956
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S
index fd5493c22b16..c4b978d67b8e 100644
--- a/xen/arch/x86/indirect-thunk.S
+++ b/xen/arch/x86/indirect-thunk.S
@@ -11,6 +11,10 @@
#include <asm/asm_defns.h>
+/* Alignment is dealt with explicitly here; override the respective macro. */
+#undef SYM_ALIGN
+#define SYM_ALIGN(align...)
+
.macro IND_THUNK_RETPOLINE reg:req
call 1f
int3
@@ -35,6 +39,16 @@
.macro GEN_INDIRECT_THUNK reg:req
.section .text.__x86_indirect_thunk_\reg, "ax", @progbits
+ /*
+ * The Indirect Target Selection speculative vulnerability means that
+ * indirect branches (including RETs) are unsafe when in the first
+ * half of a cacheline. Arrange for them to be in the second half.
+ *
+ * Align to 64, then skip 32.
+ */
+ .balign 64
+ .fill 32, 1, 0xcc
+
FUNC(__x86_indirect_thunk_\reg)
ALTERNATIVE_2 __stringify(IND_THUNK_RETPOLINE \reg), \
__stringify(IND_THUNK_LFENCE \reg), X86_FEATURE_IND_THUNK_LFENCE, \

View file

@ -1,64 +0,0 @@
From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: x86/thunk: (Mis)align the RETs in clear_bhb_loops() to mitigate ITS
The Indirect Target Selection speculative vulnerability means that indirect
branches (including RETs) are unsafe when in the first half of a cacheline.
clear_bhb_loops() has a precise layout of branches. The alignment for
performance cause the RETs to always be in an unsafe position, and converting
those to return thunks changes the branching pattern. While such a conversion
is believed to be safe, clear_bhb_loops() is also a performance-relevant
fastpath, so (mis)align the RETs to be in a safe position.
No functional change.
This is part of XSA-469 / CVE-2024-28956
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S
index 678c00c5d06f..52625f4e2c17 100644
--- a/xen/arch/x86/bhb-thunk.S
+++ b/xen/arch/x86/bhb-thunk.S
@@ -50,7 +50,12 @@ END(clear_bhb_tsx)
* ret
*
* The CALL/RETs are necessary to prevent the Loop Stream Detector from
- * interfering. The alignment is for performance and not safety.
+ * interfering.
+ *
+ * The .balign's are for performance, but they cause the RETs to be in unsafe
+ * positions with respect to Indirect Target Selection. The .skips are to
+ * move the RETs into ITS-safe positions, rather than using the slowpath
+ * through __x86_return_thunk.
*
* The "short" sequence (5 and 5) is for CPUs prior to Alder Lake / Sapphire
* Rapids (i.e. Cores prior to Golden Cove and/or Gracemont).
@@ -66,12 +71,14 @@ FUNC(clear_bhb_loops)
jmp 5f
int3
- .align 64
+ .balign 64
+ .skip 32 - (.Lr1 - 1f), 0xcc
1: call 2f
- ret
+.Lr1: ret
int3
- .align 64
+ .balign 64
+ .skip 32 - 18 /* (.Lr2 - 2f) but Clang IAS doesn't like this */, 0xcc
2: ALTERNATIVE "mov $5, %eax", "mov $7, %eax", X86_SPEC_BHB_LOOPS_LONG
3: jmp 4f
@@ -83,7 +90,7 @@ FUNC(clear_bhb_loops)
sub $1, %ecx
jnz 1b
- ret
+.Lr2: ret
5:
/*
* The Intel sequence has an LFENCE here. The purpose is to ensure

View file

@ -1,506 +0,0 @@
From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: x86/stubs: Introduce place_ret() to abstract away raw 0xc3's
The Indirect Target Selection speculative vulnerability means that indirect
branches (including RETs) are unsafe when in the first half of a cacheline.
This means it's not safe for logic using the stubs to write raw 0xc3's.
Introduce place_ret() which, for now, writes a raw 0xc3 but will contain
additional logic when return thunks are in use.
stub_selftest() doesn't strictly need to be converted as they only run on
boot, but doing so gets us a partial test of place_ret() too.
No functional change.
This is part of XSA-469 / CVE-2024-28956
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
diff --git a/tools/tests/x86_emulator/x86-emulate.h b/tools/tests/x86_emulator/x86-emulate.h
index 8f8accfe3e70..946aaa9d660b 100644
--- a/tools/tests/x86_emulator/x86-emulate.h
+++ b/tools/tests/x86_emulator/x86-emulate.h
@@ -68,6 +68,12 @@
#define is_canonical_address(x) (((int64_t)(x) >> 47) == ((int64_t)(x) >> 63))
+static inline void *place_ret(void *ptr)
+{
+ *(uint8_t *)ptr = 0xc3;
+ return ptr + 1;
+}
+
extern uint32_t mxcsr_mask;
extern struct cpu_policy cp;
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index c1e64278ce85..a7e5a82689de 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -11,9 +11,7 @@ obj-$(CONFIG_PV) += pv/
obj-y += x86_64/
obj-y += x86_emulate/
-alternative-y := alternative.init.o
-alternative-$(CONFIG_LIVEPATCH) :=
-obj-bin-y += $(alternative-y)
+obj-y += alternative.o
obj-y += apic.o
obj-y += bhb-thunk.o
obj-y += bitops.o
@@ -41,7 +39,7 @@ obj-y += hypercall.o
obj-y += i387.o
obj-y += i8259.o
obj-y += io_apic.o
-obj-$(CONFIG_LIVEPATCH) += alternative.o livepatch.o
+obj-$(CONFIG_LIVEPATCH) += livepatch.o
obj-y += msi.o
obj-y += msr.o
obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index 88c90044c20d..ec451d962c10 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -137,6 +137,20 @@ void init_or_livepatch add_nops(void *insns, unsigned int len)
}
}
+/*
+ * Place a return at @ptr. @ptr must be in the writable alias of a stub.
+ *
+ * Returns the next position to write into the stub.
+ */
+void *place_ret(void *ptr)
+{
+ uint8_t *p = ptr;
+
+ *p++ = 0xc3;
+
+ return p;
+}
+
/*
* text_poke - Update instructions on a live kernel or non-executed code.
* @addr: address to modify
diff --git a/xen/arch/x86/extable.c b/xen/arch/x86/extable.c
index 705cf9eb94ca..1572efa69a00 100644
--- a/xen/arch/x86/extable.c
+++ b/xen/arch/x86/extable.c
@@ -151,20 +151,20 @@ search_exception_table(const struct cpu_user_regs *regs, unsigned long *stub_ra)
int __init cf_check stub_selftest(void)
{
static const struct {
- uint8_t opc[8];
+ uint8_t opc[7];
uint64_t rax;
union stub_exception_token res;
} tests[] __initconst = {
#define endbr64 0xf3, 0x0f, 0x1e, 0xfa
- { .opc = { endbr64, 0x0f, 0xb9, 0xc3, 0xc3 }, /* ud1 */
+ { .opc = { endbr64, 0x0f, 0xb9, 0x90 }, /* ud1 */
.res.fields.trapnr = X86_EXC_UD },
- { .opc = { endbr64, 0x90, 0x02, 0x00, 0xc3 }, /* nop; add (%rax),%al */
+ { .opc = { endbr64, 0x90, 0x02, 0x00 }, /* nop; add (%rax),%al */
.rax = 0x0123456789abcdef,
.res.fields.trapnr = X86_EXC_GP },
- { .opc = { endbr64, 0x02, 0x04, 0x04, 0xc3 }, /* add (%rsp,%rax),%al */
+ { .opc = { endbr64, 0x02, 0x04, 0x04 }, /* add (%rsp,%rax),%al */
.rax = 0xfedcba9876543210UL,
.res.fields.trapnr = X86_EXC_SS },
- { .opc = { endbr64, 0xcc, 0xc3, 0xc3, 0xc3 }, /* int3 */
+ { .opc = { endbr64, 0xcc, 0x90, 0x90 }, /* int3 */
.res.fields.trapnr = X86_EXC_BP },
#undef endbr64
};
@@ -183,6 +183,7 @@ int __init cf_check stub_selftest(void)
memset(ptr, 0xcc, STUB_BUF_SIZE / 2);
memcpy(ptr, tests[i].opc, ARRAY_SIZE(tests[i].opc));
+ place_ret(ptr + ARRAY_SIZE(tests[i].opc));
unmap_domain_page(ptr);
asm volatile ( "INDIRECT_CALL %[stb]\n"
diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h
index 89b7bdcb82e5..841a63ebf1b6 100644
--- a/xen/arch/x86/include/asm/alternative.h
+++ b/xen/arch/x86/include/asm/alternative.h
@@ -30,6 +30,8 @@ struct __packed alt_instr {
#define ALT_REPL_PTR(a) __ALT_PTR(a, repl_offset)
extern void add_nops(void *insns, unsigned int len);
+void *place_ret(void *ptr);
+
/* Similar to alternative_instructions except it can be run with IRQs enabled. */
extern int apply_alternatives(struct alt_instr *start, struct alt_instr *end);
extern void alternative_instructions(void);
diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c
index 70150c272276..ff5d1c9f8634 100644
--- a/xen/arch/x86/pv/emul-priv-op.c
+++ b/xen/arch/x86/pv/emul-priv-op.c
@@ -76,7 +76,6 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode,
0x41, 0x5c, /* pop %r12 */
0x5d, /* pop %rbp */
0x5b, /* pop %rbx */
- 0xc3, /* ret */
};
const struct stubs *this_stubs = &this_cpu(stubs);
@@ -126,11 +125,13 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode,
APPEND_CALL(save_guest_gprs);
APPEND_BUFF(epilogue);
+ p = place_ret(p);
/* Build-time best effort attempt to catch problems. */
BUILD_BUG_ON(STUB_BUF_SIZE / 2 <
(sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ +
- MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES)));
+ MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) +
+ 1 /* ret */));
/* Runtime confirmation that we haven't clobbered an adjacent stub. */
BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub));
diff --git a/xen/arch/x86/x86_emulate/fpu.c b/xen/arch/x86/x86_emulate/fpu.c
index 480d87965705..03612d00a2ce 100644
--- a/xen/arch/x86/x86_emulate/fpu.c
+++ b/xen/arch/x86/x86_emulate/fpu.c
@@ -32,36 +32,42 @@ static inline bool fpu_check_write(void)
#define emulate_fpu_insn_memdst(opc, ext, arg) \
do { \
+ void *_p = get_stub(stub); \
/* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \
*insn_bytes = 2; \
- memcpy(get_stub(stub), \
- ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3); \
+ memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \
+ place_ret(_p); \
invoke_stub("", "", "+m" (arg) : "a" (&(arg))); \
put_stub(stub); \
} while (0)
#define emulate_fpu_insn_memsrc(opc, ext, arg) \
do { \
+ void *_p = get_stub(stub); \
/* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \
- memcpy(get_stub(stub), \
- ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3); \
+ memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \
+ place_ret(_p); \
invoke_stub("", "", "=m" (dummy) : "m" (arg), "a" (&(arg))); \
put_stub(stub); \
} while (0)
#define emulate_fpu_insn_stub(bytes...) \
do { \
+ void *_p = get_stub(stub); \
unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \
- memcpy(get_stub(stub), ((uint8_t[]){ bytes, 0xc3 }), nr_ + 1); \
+ memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \
+ place_ret(_p); \
invoke_stub("", "", "=m" (dummy) : "i" (0)); \
put_stub(stub); \
} while (0)
#define emulate_fpu_insn_stub_eflags(bytes...) \
do { \
+ void *_p = get_stub(stub); \
unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \
unsigned long tmp_; \
- memcpy(get_stub(stub), ((uint8_t[]){ bytes, 0xc3 }), nr_ + 1); \
+ memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \
+ place_ret(_p); \
invoke_stub(_PRE_EFLAGS("[eflags]", "[mask]", "[tmp]"), \
_POST_EFLAGS("[eflags]", "[mask]", "[tmp]"), \
[eflags] "+g" (regs->eflags), [tmp] "=&r" (tmp_) \
diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c
index b1d192cbbf1e..f40709682484 100644
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1396,7 +1396,7 @@ x86_emulate(
stb[3] = 0x91;
stb[4] = evex.opmsk << 3;
insn_bytes = 5;
- stb[5] = 0xc3;
+ place_ret(&stb[5]);
invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask));
@@ -3627,7 +3627,7 @@ x86_emulate(
}
opc[1] = (modrm & 0x38) | 0xc0;
insn_bytes = EVEX_PFX_BYTES + 2;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_EVEX(opc, evex);
invoke_stub("", "", "=g" (dummy) : "a" (src.val));
@@ -3694,7 +3694,7 @@ x86_emulate(
insn_bytes = PFX_BYTES + 2;
copy_REX_VEX(opc, rex_prefix, vex);
}
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
ea.reg = decode_gpr(&_regs, modrm_reg);
invoke_stub("", "", "=a" (*ea.reg) : "c" (mmvalp), "m" (*mmvalp));
@@ -3768,7 +3768,7 @@ x86_emulate(
insn_bytes = PFX_BYTES + 2;
copy_REX_VEX(opc, rex_prefix, vex);
}
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
_regs.eflags &= ~EFLAGS_MASK;
invoke_stub("",
@@ -4004,7 +4004,7 @@ x86_emulate(
opc[1] = modrm & 0xc7;
insn_bytes = PFX_BYTES + 2;
simd_0f_to_gpr:
- opc[insn_bytes - PFX_BYTES] = 0xc3;
+ place_ret(&opc[insn_bytes - PFX_BYTES]);
generate_exception_if(ea.type != OP_REG, X86_EXC_UD);
@@ -4401,7 +4401,7 @@ x86_emulate(
vex.w = 0;
opc[1] = modrm & 0x38;
insn_bytes = PFX_BYTES + 2;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_REX_VEX(opc, rex_prefix, vex);
invoke_stub("", "", "+m" (src.val) : "a" (&src.val));
@@ -4438,7 +4438,7 @@ x86_emulate(
evex.w = 0;
opc[1] = modrm & 0x38;
insn_bytes = EVEX_PFX_BYTES + 2;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_EVEX(opc, evex);
invoke_stub("", "", "+m" (src.val) : "a" (&src.val));
@@ -4633,7 +4633,7 @@ x86_emulate(
#endif /* X86EMUL_NO_SIMD */
simd_0f_reg_only:
- opc[insn_bytes - PFX_BYTES] = 0xc3;
+ place_ret(&opc[insn_bytes - PFX_BYTES]);
copy_REX_VEX(opc, rex_prefix, vex);
invoke_stub("", "", [dummy_out] "=g" (dummy) : [dummy_in] "i" (0) );
@@ -4967,7 +4967,7 @@ x86_emulate(
if ( !mode_64bit() )
vex.w = 0;
opc[1] = modrm & 0xf8;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_VEX(opc, vex);
ea.reg = decode_gpr(&_regs, modrm_rm);
@@ -5010,7 +5010,7 @@ x86_emulate(
if ( !mode_64bit() )
vex.w = 0;
opc[1] = modrm & 0xc7;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_VEX(opc, vex);
invoke_stub("", "", "=a" (dst.val) : [dummy] "i" (0));
@@ -5040,7 +5040,7 @@ x86_emulate(
opc = init_prefixes(stub);
opc[0] = b;
opc[1] = modrm;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_VEX(opc, vex);
_regs.eflags &= ~EFLAGS_MASK;
@@ -5608,7 +5608,7 @@ x86_emulate(
if ( !mode_64bit() )
vex.w = 0;
opc[1] = modrm & 0xc7;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_REX_VEX(opc, rex_prefix, vex);
invoke_stub("", "", "=a" (ea.val) : [dummy] "i" (0));
@@ -5726,7 +5726,7 @@ x86_emulate(
opc[1] &= 0x38;
}
insn_bytes = PFX_BYTES + 2;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
if ( vex.opcx == vex_none )
{
/* Cover for extra prefix byte. */
@@ -6006,7 +6006,7 @@ x86_emulate(
pvex->b = !mode_64bit() || (vex.reg >> 3);
opc[1] = 0xc0 | (~vex.reg & 7);
pvex->reg = 0xf;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "=a" (ea.val) : [dummy] "i" (0));
put_stub(stub);
@@ -6290,7 +6290,7 @@ x86_emulate(
evex.w = 0;
opc[1] = modrm & 0xf8;
insn_bytes = EVEX_PFX_BYTES + 2;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
copy_EVEX(opc, evex);
invoke_stub("", "", "=g" (dummy) : "a" (src.val));
@@ -6389,7 +6389,7 @@ x86_emulate(
pvex->b = 1;
opc[1] = (modrm_reg & 7) << 3;
pvex->reg = 0xf;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp));
@@ -6459,7 +6459,7 @@ x86_emulate(
pvex->b = 1;
opc[1] = (modrm_reg & 7) << 3;
pvex->reg = 0xf;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "+m" (*mmvalp) : "a" (mmvalp));
@@ -6515,7 +6515,7 @@ x86_emulate(
pevex->b = 1;
opc[1] = (modrm_reg & 7) << 3;
pevex->RX = 1;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp));
@@ -6580,7 +6580,7 @@ x86_emulate(
pevex->b = 1;
opc[1] = (modrm_reg & 7) << 3;
pevex->RX = 1;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "+m" (*mmvalp) : "a" (mmvalp));
@@ -6594,7 +6594,7 @@ x86_emulate(
opc[2] = 0x90;
/* Use (%rax) as source. */
opc[3] = evex.opmsk << 3;
- opc[4] = 0xc3;
+ place_ret(&opc[4]);
invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask));
put_stub(stub);
@@ -6688,7 +6688,7 @@ x86_emulate(
pevex->b = 1;
opc[1] = (modrm_reg & 7) << 3;
pevex->RX = 1;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp));
@@ -6766,7 +6766,7 @@ x86_emulate(
opc[2] = 0x90;
/* Use (%rax) as source. */
opc[3] = evex.opmsk << 3;
- opc[4] = 0xc3;
+ place_ret(&opc[4]);
invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask));
put_stub(stub);
@@ -6848,7 +6848,7 @@ x86_emulate(
pevex->r = !mode_64bit() || !(state->sib_index & 0x08);
pevex->R = !mode_64bit() || !(state->sib_index & 0x10);
pevex->RX = 1;
- opc[2] = 0xc3;
+ place_ret(&opc[2]);
invoke_stub("", "", "=m" (index) : "a" (&index));
put_stub(stub);
@@ -7058,7 +7058,7 @@ x86_emulate(
pvex->reg = 0xf; /* rAX */
buf[3] = b;
buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */
- buf[5] = 0xc3;
+ place_ret(&buf[5]);
src.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val), "a" (*src.reg));
@@ -7094,7 +7094,7 @@ x86_emulate(
pvex->reg = 0xf; /* rAX */
buf[3] = b;
buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */
- buf[5] = 0xc3;
+ place_ret(&buf[5]);
dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
emulate_stub("=&a" (dst.val), "c" (&src.val));
@@ -7335,7 +7335,7 @@ x86_emulate(
evex.w = vex.w = 0;
opc[1] = modrm & 0x38;
opc[2] = imm1;
- opc[3] = 0xc3;
+ place_ret(&opc[3]);
if ( vex.opcx == vex_none )
{
/* Cover for extra prefix byte. */
@@ -7502,7 +7502,7 @@ x86_emulate(
insn_bytes = PFX_BYTES + 3;
copy_VEX(opc, vex);
}
- opc[3] = 0xc3;
+ place_ret(&opc[3]);
/* Latch MXCSR - we may need to restore it below. */
invoke_stub("stmxcsr %[mxcsr]", "",
@@ -7748,7 +7748,7 @@ x86_emulate(
}
opc[2] = imm1;
insn_bytes = PFX_BYTES + 3;
- opc[3] = 0xc3;
+ place_ret(&opc[3]);
if ( vex.opcx == vex_none )
{
/* Cover for extra prefix byte. */
@@ -8094,7 +8094,7 @@ x86_emulate(
pxop->reg = 0xf; /* rAX */
buf[3] = b;
buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */
- buf[5] = 0xc3;
+ place_ret(&buf[5]);
dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt);
emulate_stub([dst] "=&a" (dst.val), "c" (&src.val));
@@ -8203,7 +8203,7 @@ x86_emulate(
buf[3] = b;
buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */
*(uint32_t *)(buf + 5) = imm1;
- buf[9] = 0xc3;
+ place_ret(&buf[9]);
emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val));
@@ -8293,12 +8293,12 @@ x86_emulate(
BUG();
if ( evex_encoded() )
{
- opc[insn_bytes - EVEX_PFX_BYTES] = 0xc3;
+ place_ret(&opc[insn_bytes - EVEX_PFX_BYTES]);
copy_EVEX(opc, evex);
}
else
{
- opc[insn_bytes - PFX_BYTES] = 0xc3;
+ place_ret(&opc[insn_bytes - PFX_BYTES]);
copy_REX_VEX(opc, rex_prefix, vex);
}

View file

@ -1,375 +0,0 @@
From: Jan Beulich <jbeulich@suse.com>
Subject: x86/thunk: Build Xen with Return Thunks
The Indirect Target Selection speculative vulnerability means that indirect
branches (including RETs) are unsafe when in the first half of a cacheline.
In order to mitigate this, build with return thunks and arrange for
__x86_return_thunk to be (mis)aligned in the same manner as
__x86_indirect_thunk_* so the RET instruction is placed in a safe location.
place_ret() needs to conditionally emit JMP __x86_return_thunk instead of RET.
This is part of XSA-469 / CVE-2024-28956
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index 7e03e4bc5546..4542ea8408c7 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -37,9 +37,14 @@ config ARCH_DEFCONFIG
default "arch/x86/configs/x86_64_defconfig"
config CC_HAS_INDIRECT_THUNK
+ # GCC >= 8 or Clang >= 6
def_bool $(cc-option,-mindirect-branch-register) || \
$(cc-option,-mretpoline-external-thunk)
+config CC_HAS_RETURN_THUNK
+ # GCC >= 8 or Clang >= 15
+ def_bool $(cc-option,-mfunction-return=thunk-extern)
+
config HAS_AS_CET_SS
# binutils >= 2.29 or LLVM >= 6
def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy)
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index a7e5a82689de..27806a81aca8 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o
obj-y += msi.o
obj-y += msr.o
obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o
+obj-$(CONFIG_RETURN_THUNK) += indirect-thunk.o
obj-$(CONFIG_PV) += ioport_emulate.o
obj-y += irq.o
obj-$(CONFIG_KEXEC) += machine_kexec.o
diff --git a/xen/arch/x86/acpi/wakeup_prot.S b/xen/arch/x86/acpi/wakeup_prot.S
index 66f799339913..97bd676aaee2 100644
--- a/xen/arch/x86/acpi/wakeup_prot.S
+++ b/xen/arch/x86/acpi/wakeup_prot.S
@@ -133,7 +133,7 @@ ENTRY(s3_resume)
pop %r12
pop %rbx
pop %rbp
- ret
+ RET
.data
.align 16
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index ec451d962c10..1b71ae959abe 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -137,16 +137,45 @@ void init_or_livepatch add_nops(void *insns, unsigned int len)
}
}
+void nocall __x86_return_thunk(void);
+
/*
* Place a return at @ptr. @ptr must be in the writable alias of a stub.
*
+ * When CONFIG_RETURN_THUNK is active, this may be a JMP __x86_return_thunk
+ * instead, depending on the safety of @ptr with respect to Indirect Target
+ * Selection.
+ *
* Returns the next position to write into the stub.
*/
void *place_ret(void *ptr)
{
+ unsigned long addr = (unsigned long)ptr;
uint8_t *p = ptr;
- *p++ = 0xc3;
+ /*
+ * When Return Thunks are used, if a RET would be unsafe at this location
+ * with respect to Indirect Target Selection (i.e. if addr is in the first
+ * half of a cacheline), insert a JMP __x86_return_thunk instead.
+ *
+ * The displacement needs to be relative to the executable alias of the
+ * stub, not to @ptr which is the writeable alias.
+ */
+ if ( IS_ENABLED(CONFIG_RETURN_THUNK) && !(addr & 0x20) )
+ {
+ long stub_va = (this_cpu(stubs.addr) & PAGE_MASK) + (addr & ~PAGE_MASK);
+ long disp = (long)__x86_return_thunk - (stub_va + 5);
+
+ BUG_ON((int32_t)disp != disp);
+
+ *p++ = 0xe9;
+ *(int32_t *)p = disp;
+ p += 4;
+ }
+ else
+ {
+ *p++ = 0xc3;
+ }
return p;
}
diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk
index b88d097a844b..85d3e7cbfeeb 100644
--- a/xen/arch/x86/arch.mk
+++ b/xen/arch/x86/arch.mk
@@ -46,6 +46,9 @@ CFLAGS-$(CONFIG_CC_IS_GCC) += -fno-jump-tables
CFLAGS-$(CONFIG_CC_IS_CLANG) += -mretpoline-external-thunk
endif
+# Compile with return thunk support if selected.
+CFLAGS-$(CONFIG_RETURN_THUNK) += -mfunction-return=thunk-extern
+
# Disable the addition of a .note.gnu.property section to object files when
# livepatch support is enabled. The contents of that section can change
# depending on the instructions used, and livepatch-build-tools doesn't know
diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S
index 52625f4e2c17..7f92201a3cbb 100644
--- a/xen/arch/x86/bhb-thunk.S
+++ b/xen/arch/x86/bhb-thunk.S
@@ -23,7 +23,7 @@ FUNC(clear_bhb_tsx)
0: .byte 0xc6, 0xf8, 0 /* xabort $0 */
int3
1:
- ret
+ RET
END(clear_bhb_tsx)
/*
diff --git a/xen/arch/x86/clear_page.S b/xen/arch/x86/clear_page.S
index d6c076f1d8bc..dc3c3c26bfb7 100644
--- a/xen/arch/x86/clear_page.S
+++ b/xen/arch/x86/clear_page.S
@@ -1,6 +1,8 @@
.file __FILE__
#include <xen/linkage.h>
+
+#include <asm/asm_defns.h>
#include <asm/page.h>
FUNC(clear_page_sse2)
@@ -16,5 +18,5 @@ FUNC(clear_page_sse2)
jnz 0b
sfence
- ret
+ RET
END(clear_page_sse2)
diff --git a/xen/arch/x86/copy_page.S b/xen/arch/x86/copy_page.S
index c3c436545bac..e43e5370c815 100644
--- a/xen/arch/x86/copy_page.S
+++ b/xen/arch/x86/copy_page.S
@@ -1,6 +1,8 @@
.file __FILE__
#include <xen/linkage.h>
+
+#include <asm/asm_defns.h>
#include <asm/page.h>
#define src_reg %rsi
@@ -41,5 +43,5 @@ FUNC(copy_page_sse2)
movnti tmp4_reg, 3*WORD_SIZE(dst_reg)
sfence
- ret
+ RET
END(copy_page_sse2)
diff --git a/xen/arch/x86/efi/check.c b/xen/arch/x86/efi/check.c
index 9e473faad3c9..23ba30abf330 100644
--- a/xen/arch/x86/efi/check.c
+++ b/xen/arch/x86/efi/check.c
@@ -3,6 +3,9 @@ int __attribute__((__ms_abi__)) test(int i)
return i;
}
+/* In case -mfunction-return is in use. */
+void __x86_return_thunk(void) {};
+
/*
* Populate an array with "addresses" of relocatable and absolute values.
* This is to probe ld for (a) emitting base relocations at all and (b) not
diff --git a/xen/arch/x86/include/asm/asm-defns.h b/xen/arch/x86/include/asm/asm-defns.h
index 32d6b4491063..97ebe21298a2 100644
--- a/xen/arch/x86/include/asm/asm-defns.h
+++ b/xen/arch/x86/include/asm/asm-defns.h
@@ -58,6 +58,12 @@
.endif
.endm
+#ifdef CONFIG_RETURN_THUNK
+# define RET jmp __x86_return_thunk
+#else
+# define RET ret
+#endif
+
#ifdef CONFIG_XEN_IBT
# define ENDBR64 endbr64
#else
diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S
index c4b978d67b8e..26dad15f12c9 100644
--- a/xen/arch/x86/indirect-thunk.S
+++ b/xen/arch/x86/indirect-thunk.S
@@ -15,6 +15,8 @@
#undef SYM_ALIGN
#define SYM_ALIGN(align...)
+#ifdef CONFIG_INDIRECT_THUNK
+
.macro IND_THUNK_RETPOLINE reg:req
call 1f
int3
@@ -62,3 +64,25 @@ END(__x86_indirect_thunk_\reg)
.irp reg, ax, cx, dx, bx, bp, si, di, 8, 9, 10, 11, 12, 13, 14, 15
GEN_INDIRECT_THUNK reg=r\reg
.endr
+
+#endif /* CONFIG_INDIRECT_THUNK */
+
+#ifdef CONFIG_RETURN_THUNK
+ .section .text.entry.__x86_return_thunk, "ax", @progbits
+
+ /*
+ * The Indirect Target Selection speculative vulnerability means that
+ * indirect branches (including RETs) are unsafe when in the first
+ * half of a cacheline. Arrange for them to be in the second half.
+ *
+ * Align to 64, then skip 32.
+ */
+ .balign 64
+ .fill 32, 1, 0xcc
+
+FUNC(__x86_return_thunk)
+ ret
+ int3 /* Halt straight-line speculation */
+END(__x86_return_thunk)
+
+#endif /* CONFIG_RETURN_THUNK */
diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c
index ff5d1c9f8634..295d847ea24c 100644
--- a/xen/arch/x86/pv/emul-priv-op.c
+++ b/xen/arch/x86/pv/emul-priv-op.c
@@ -131,7 +131,7 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode,
BUILD_BUG_ON(STUB_BUF_SIZE / 2 <
(sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ +
MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) +
- 1 /* ret */));
+ (IS_ENABLED(CONFIG_RETURN_THUNK) ? 5 : 1) /* ret */));
/* Runtime confirmation that we haven't clobbered an adjacent stub. */
BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub));
diff --git a/xen/arch/x86/pv/gpr_switch.S b/xen/arch/x86/pv/gpr_switch.S
index 5409ad3b1447..362b5d241623 100644
--- a/xen/arch/x86/pv/gpr_switch.S
+++ b/xen/arch/x86/pv/gpr_switch.S
@@ -26,7 +26,7 @@ FUNC(load_guest_gprs)
movq UREGS_r15(%rdi), %r15
movq UREGS_rcx(%rdi), %rcx
movq UREGS_rdi(%rdi), %rdi
- ret
+ RET
END(load_guest_gprs)
/* Save guest GPRs. Parameter on the stack above the return address. */
@@ -48,5 +48,5 @@ FUNC(save_guest_gprs)
movq %rbx, UREGS_rbx(%rdi)
movq %rdx, UREGS_rdx(%rdi)
movq %rcx, UREGS_rcx(%rdi)
- ret
+ RET
END(save_guest_gprs)
diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c
index 35351044f901..019a0a81f4a7 100644
--- a/xen/arch/x86/spec_ctrl.c
+++ b/xen/arch/x86/spec_ctrl.c
@@ -569,6 +569,9 @@ static void __init print_details(enum ind_thunk thunk)
#ifdef CONFIG_INDIRECT_THUNK
" INDIRECT_THUNK"
#endif
+#ifdef CONFIG_RETURN_THUNK
+ " RETURN_THUNK"
+#endif
#ifdef CONFIG_SHADOW_PAGING
" SHADOW_PAGING"
#endif
diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S
index a99646c0cd4e..18f46c78cfbe 100644
--- a/xen/arch/x86/x86_64/compat/entry.S
+++ b/xen/arch/x86/x86_64/compat/entry.S
@@ -180,7 +180,7 @@ FUNC(cr4_pv32_restore)
or cr4_pv32_mask(%rip), %rax
mov %rax, %cr4
mov %rax, (%rcx)
- ret
+ RET
0:
#ifndef NDEBUG
/* Check that _all_ of the bits intended to be set actually are. */
@@ -198,7 +198,7 @@ FUNC(cr4_pv32_restore)
1:
#endif
xor %eax, %eax
- ret
+ RET
END(cr4_pv32_restore)
FUNC(compat_syscall)
@@ -329,7 +329,7 @@ __UNLIKELY_END(compat_bounce_null_selector)
xor %eax, %eax
mov %ax, TRAPBOUNCE_cs(%rdx)
mov %al, TRAPBOUNCE_flags(%rdx)
- ret
+ RET
.section .fixup,"ax"
.Lfx13:
diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S
index 9b0cdb76408b..eb62e7c329bd 100644
--- a/xen/arch/x86/x86_64/entry.S
+++ b/xen/arch/x86/x86_64/entry.S
@@ -604,7 +604,7 @@ __UNLIKELY_END(create_bounce_frame_bad_bounce_ip)
xor %eax, %eax
mov %rax, TRAPBOUNCE_eip(%rdx)
mov %al, TRAPBOUNCE_flags(%rdx)
- ret
+ RET
.pushsection .fixup, "ax", @progbits
# Numeric tags below represent the intended overall %rsi adjustment.
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 9a1dfe1b340a..506993867502 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -82,6 +82,7 @@ SECTIONS
. = ALIGN(PAGE_SIZE);
_stextentry = .;
*(.text.entry)
+ *(.text.entry.*)
. = ALIGN(PAGE_SIZE);
_etextentry = .;
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 565ceda741b9..da0fa7527643 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -130,6 +130,17 @@ config INDIRECT_THUNK
When enabled, indirect branches are implemented using a new construct
called "retpoline" that prevents speculation.
+config RETURN_THUNK
+ bool "Out-of-line Returns"
+ depends on CC_HAS_RETURN_THUNK
+ default INDIRECT_THUNK
+ help
+ Compile Xen with out-of-line returns.
+
+ This allows Xen to mitigate a variety of speculative vulnerabilities
+ by choosing a hardware-dependent instruction sequence to implement
+ function returns safely.
+
config SPECULATIVE_HARDEN_ARRAY
bool "Speculative Array Hardening"
default y

View file

@ -1,154 +0,0 @@
From: Andrew Cooper <andrew.cooper3@citrix.com>
Subject: x86/spec-ctrl: Synthesise ITS_NO to guests on unaffected hardware
It is easier to express feature word 17 in terms of word 16 + [32, 64) as
that's how the layout is given in documentation.
This is part of XSA-469 / CVE-2024-28956
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h
index 9bc553681f4a..1729ba0c3097 100644
--- a/xen/arch/x86/include/asm/cpufeature.h
+++ b/xen/arch/x86/include/asm/cpufeature.h
@@ -216,6 +216,7 @@ static inline bool boot_cpu_has(unsigned int feat)
#define cpu_has_gds_no boot_cpu_has(X86_FEATURE_GDS_NO)
#define cpu_has_rfds_no boot_cpu_has(X86_FEATURE_RFDS_NO)
#define cpu_has_rfds_clear boot_cpu_has(X86_FEATURE_RFDS_CLEAR)
+#define cpu_has_its_no boot_cpu_has(X86_FEATURE_ITS_NO)
/* Synthesized. */
#define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c
index 019a0a81f4a7..94cdbd521c4d 100644
--- a/xen/arch/x86/spec_ctrl.c
+++ b/xen/arch/x86/spec_ctrl.c
@@ -1781,6 +1781,90 @@ static void __init bhi_calculations(void)
}
}
+/*
+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/indirect-target-selection.html
+ */
+static void __init its_calculations(void)
+{
+ /*
+ * Indirect Target Selection is a Branch Prediction bug whereby certain
+ * indirect branches (including RETs) get predicted using a direct branch
+ * target, rather than a suitable indirect target, bypassing hardware
+ * isolation protections.
+ *
+ * ITS affects Core (but not Atom) processors starting from the
+ * introduction of eIBRS, up to but not including Golden Cove cores
+ * (checked here with BHI_CTRL).
+ *
+ * The ITS_NO feature is not expected to be enumerated by hardware, and is
+ * only for VMMs to synthesise for guests.
+ *
+ * ITS comes in 3 flavours:
+ *
+ * 1) Across-IBPB. Indirect branches after the IBPB can be controlled
+ * by direct targets which existed prior to the IBPB. This is
+ * addressed in the IPU 2025.1 microcode drop, and has no other
+ * software interaction.
+ *
+ * 2) Guest/Host. Indirect branches in the VMM can be controlled by
+ * direct targets from the guest. This applies equally to PV guests
+ * (Ring3) and HVM guests (VMX), and applies to all Skylake-uarch
+ * cores with eIBRS.
+ *
+ * 3) Intra-mode. Indirect branches in the VMM can be controlled by
+ * other execution in the same mode.
+ */
+
+ /*
+ * If we can see ITS_NO, or we're virtualised, do nothing. We are or may
+ * migrate somewhere unsafe.
+ */
+ if ( cpu_has_its_no || cpu_has_hypervisor )
+ return;
+
+ /* ITS is only known to affect Intel processors at this time. */
+ if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL )
+ return;
+
+ /*
+ * ITS does not exist on:
+ * - non-Family 6 CPUs
+ * - those without eIBRS
+ * - those with BHI_CTRL
+ * but we still need to synthesise ITS_NO.
+ */
+ if ( boot_cpu_data.x86 != 6 || !cpu_has_eibrs ||
+ boot_cpu_has(X86_FEATURE_BHI_CTRL) )
+ goto synthesise;
+
+ switch ( boot_cpu_data.x86_model )
+ {
+ /* These Skylake-uarch cores suffer cases #2 and #3. */
+ case INTEL_FAM6_SKYLAKE_X:
+ case INTEL_FAM6_KABYLAKE_L:
+ case INTEL_FAM6_KABYLAKE:
+ case INTEL_FAM6_COMETLAKE:
+ case INTEL_FAM6_COMETLAKE_L:
+ return;
+
+ /* These Sunny/Willow/Cypress Cove cores suffer case #3. */
+ case INTEL_FAM6_ICELAKE_X:
+ case INTEL_FAM6_ICELAKE_D:
+ case INTEL_FAM6_ICELAKE_L:
+ case INTEL_FAM6_TIGERLAKE_L:
+ case INTEL_FAM6_TIGERLAKE:
+ case INTEL_FAM6_ROCKETLAKE:
+ return;
+
+ default:
+ break;
+ }
+
+ /* Platforms remaining are not believed to be vulnerable to ITS. */
+ synthesise:
+ setup_force_cpu_cap(X86_FEATURE_ITS_NO);
+}
+
void spec_ctrl_init_domain(struct domain *d)
{
bool pv = is_pv_domain(d);
@@ -2331,6 +2415,8 @@ void __init init_speculation_mitigations(void)
bhi_calculations();
+ its_calculations();
+
print_details(thunk);
/*
diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h
index 9c98e4992861..4d9e468af653 100644
--- a/xen/include/public/arch-x86/cpufeatureset.h
+++ b/xen/include/public/arch-x86/cpufeatureset.h
@@ -365,7 +365,8 @@ XEN_CPUFEATURE(GDS_NO, 16*32+26) /*A No Gather Data Sampling */
XEN_CPUFEATURE(RFDS_NO, 16*32+27) /*A No Register File Data Sampling */
XEN_CPUFEATURE(RFDS_CLEAR, 16*32+28) /*!A| Register File(s) cleared by VERW */
-/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 */
+/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 (express in terms of word 16) */
+XEN_CPUFEATURE(ITS_NO, 16*32+62) /*!A No Indirect Target Selection */
#endif /* XEN_CPUFEATURE */
diff --git a/xen/tools/gen-cpuid.py b/xen/tools/gen-cpuid.py
index 601eec608983..dc33ca3181b1 100755
--- a/xen/tools/gen-cpuid.py
+++ b/xen/tools/gen-cpuid.py
@@ -51,7 +51,7 @@ def parse_definitions(state):
r"\s+/\*([\w!|]*) .*$")
word_regex = re.compile(
- r"^/\* .* word (\d*) \*/$")
+ r"^/\* .* word (\d*) .*\*/$")
last_word = -1
this = sys.modules[__name__]

30
xsa483.patch Normal file
View file

@ -0,0 +1,30 @@
From: Andrii Sultanov <andriy.sultanov@vates.tech>
Subject: tools/oxenstored: Reset quota when resetting permissions
The quota object contains both limits and the current node usage counts.
When a domain is torn down, the node data itself is cleaned up but the node
usage counts are not. A later domain reusing the same domid can create fewer
nodes before being deemed to be over quota.
Reset the count when the node permissions are cleaned up.
This is XSA-483 / CVE-2026-23556.
Signed-off-by: Andrii Sultanov <andriy.sultanov@vates.tech>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml
index 9b8dd2812df0..aa9204ead3ec 100644
--- a/tools/ocaml/xenstored/store.ml
+++ b/tools/ocaml/xenstored/store.ml
@@ -465,7 +465,8 @@ let reset_permissions store domid =
if perms <> node.perms then
Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node);
Some { node with Node.perms }
- ) store.root
+ ) store.root;
+ store.quota <- Quota.del store.quota domid
type ops = {
store: t;

89
xsa484.patch Normal file
View file

@ -0,0 +1,89 @@
From 3d0d19ad17f29c64dde4a7baf392da4fd58f3654 Mon Sep 17 00:00:00 2001
From: Juergen Gross <jgross@suse.com>
Date: Mon, 16 Mar 2026 15:06:11 +0100
Subject: [PATCH] tools/xenstored: make conn_delete_all_transactions()
idempotent
conn_delete_all_transactions() should be callable in any context,
resetting ALL transaction related data.
This includes number of active transactions and the transaction
pointer in struct connection.
So reset conn->trans to NULL in conn_delete_all_transactions() and
do the cleanup for each transaction in destroy_transaction().
This avoids triggering the assert() in conn_delete_all_transactions()
in case e.g. ignore_connection() was called while an operation inside
a transaction was performed, or XS_RESET_WATCHES was called in a
transaction.
This is XSA-484 / CVE-2026-23557.
Reported-by: Andrii Sultanov <andriy.sultanov@vates.tech>
Fixes: 1f9d04fb021c ("xenstored: allow guest to shutdown all its watches/transactions")
Signed-off-by: Juergen Gross <jgross@suse.com>
---
tools/xenstored/transaction.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/tools/xenstored/transaction.c b/tools/xenstored/transaction.c
index 167cd597fd..0825c48859 100644
--- a/tools/xenstored/transaction.c
+++ b/tools/xenstored/transaction.c
@@ -432,17 +432,23 @@ static int finalize_transaction(struct connection *conn,
static int destroy_transaction(void *_transaction)
{
struct transaction *trans = _transaction;
+ struct connection *conn = trans->conn;
struct accessed_node *i;
wrl_ntransactions--;
trace_destroy(trans, "transaction");
while ((i = list_top(&trans->accessed, struct accessed_node, list))) {
if (i->ta_node)
- db_delete(trans->conn, i->trans_name, NULL);
+ db_delete(conn, i->trans_name, NULL);
list_del(&i->list);
talloc_free(i);
}
+ list_del(&trans->list);
+ domain_transaction_dec(conn);
+ if (list_empty(&conn->transaction_list))
+ conn->ta_start_time = 0;
+
return 0;
}
@@ -523,10 +529,6 @@ int do_transaction_end(const void *ctx, struct connection *conn,
return ENOENT;
conn->transaction = NULL;
- list_del(&trans->list);
- domain_transaction_dec(conn);
- if (list_empty(&conn->transaction_list))
- conn->ta_start_time = 0;
chk_quota = trans->node_created && domain_is_unprivileged(conn);
@@ -572,14 +574,10 @@ void conn_delete_all_transactions(struct connection *conn)
struct transaction *trans;
while ((trans = list_top(&conn->transaction_list,
- struct transaction, list))) {
- list_del(&trans->list);
+ struct transaction, list)))
talloc_free(trans);
- }
-
- assert(conn->transaction == NULL);
- conn->ta_start_time = 0;
+ conn->transaction = NULL;
}
int check_transactions(struct hashtable *hash)
--
2.53.0

181
xsa486.patch Normal file
View file

@ -0,0 +1,181 @@
From: Jan Beulich <jbeulich@suse.com>
Subject: gnttab: split gnttab_map_frame()
If a domain tries to map status frames in parallel to switching grant
table version from 2 to 1, the mapping operation may put in place P2M
entries referencing MFNs which gnttab_unpopulate_status_frames() is in the
process of freeing.
Ideally we would refcount pages when entered into P2M tables, but that's a
significant change. Extend the grant-table-locked region instead in
xenmem_add_to_physmap_one() (being the sole caller of gnttab_map_frame()),
such that a race with gnttab_unpopulate_status_frames() is no longer
possible.
This is XSA-486 / CVE-2026-23558.
Fixes: 5ce8fafa947c ("Dynamic grant-table sizing")
Fixes: a98dc13703e0 ("Introduce a grant_entry_v2 structure")
Reported-by: Rafal Wojtczuk <rafal.wojtczuk@7bulls.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -174,12 +174,10 @@ int xenmem_add_to_physmap_one(
switch ( space )
{
case XENMAPSPACE_grant_table:
- rc = gnttab_map_frame(d, idx, gfn, &mfn);
+ rc = gnttab_map_frame_begin(d, idx, gfn, &mfn);
if ( rc )
return rc;
- /* Need to take care of the reference obtained in gnttab_map_frame(). */
- page = mfn_to_page(mfn);
t = p2m_ram_rw;
break;
@@ -281,10 +279,23 @@ int xenmem_add_to_physmap_one(
* to drop the reference we took earlier. In all other cases we need to
* drop any reference we took earlier (perhaps indirectly).
*/
- if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL )
+ switch ( space )
{
+ default:
+ if ( page )
+ put_page(page);
+ break;
+
+ case XENMAPSPACE_grant_table:
+ gnttab_map_frame_end(d, mfn);
+ break;
+
+ case XENMAPSPACE_gmfn_foreign:
+ if ( !rc )
+ break;
ASSERT(page != NULL);
put_page(page);
+ break;
}
return rc;
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -2009,11 +2009,9 @@ int xenmem_add_to_physmap_one(
break;
case XENMAPSPACE_grant_table:
- rc = gnttab_map_frame(d, idx, gfn, &mfn);
+ rc = gnttab_map_frame_begin(d, idx, gfn, &mfn);
if ( rc )
return rc;
- /* Need to take care of the reference obtained in gnttab_map_frame(). */
- page = mfn_to_page(mfn);
break;
case XENMAPSPACE_gmfn:
@@ -2095,19 +2093,28 @@ int xenmem_add_to_physmap_one(
put_gfn(d, gfn_x(gfn));
put_both:
- /*
- * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top.
- * We also may need to transfer ownership of the page reference to our
- * caller.
- */
- if ( space == XENMAPSPACE_gmfn )
+ switch ( space )
{
+ case XENMAPSPACE_gmfn:
+ /*
+ * We took a ref of the gfn at the top. We also may need to transfer
+ * ownership of the page reference to our caller.
+ */
put_gfn(d, gmfn);
if ( !rc && extra.ppage )
{
*extra.ppage = page;
page = NULL;
}
+ break;
+
+ case XENMAPSPACE_grant_table:
+ /*
+ * We (gnttab_map_frame_begin()) acquired a lock and took a ref of the
+ * page underlying the MFN at the top.
+ */
+ gnttab_map_frame_end(d, mfn);
+ break;
}
if ( page )
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -4250,7 +4250,8 @@ int gnttab_acquire_resource(
return rc;
}
-int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, mfn_t *mfn)
+int gnttab_map_frame_begin(
+ struct domain *d, unsigned long idx, gfn_t gfn, mfn_t *mfn)
{
int rc = 0;
struct grant_table *gt = d->grant_table;
@@ -4288,11 +4289,19 @@ int gnttab_map_frame(struct domain *d, u
put_page(pg);
}
- grant_write_unlock(gt);
+ if ( rc )
+ grant_write_unlock(d->grant_table);
return rc;
}
+void gnttab_map_frame_end(struct domain *d, mfn_t mfn)
+{
+ put_page(mfn_to_page(mfn));
+
+ grant_write_unlock(d->grant_table);
+}
+
static void gnttab_usage_print(struct domain *rd)
{
int first = 1;
--- a/xen/include/xen/grant_table.h
+++ b/xen/include/xen/grant_table.h
@@ -60,8 +60,13 @@ int gnttab_release_mappings(struct domai
int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
gfn_t *gfn, uint16_t *status);
-int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn,
- mfn_t *mfn);
+/*
+ * These need to be used as a pair, as the first (in the success case) returns
+ * with a lock and page reference held which the second needs to drop.
+ */
+int gnttab_map_frame_begin(struct domain *d, unsigned long idx, gfn_t gfn,
+ mfn_t *mfn);
+void gnttab_map_frame_end(struct domain *d, mfn_t mfn);
unsigned int gnttab_resource_max_frames(const struct domain *d, unsigned int id);
@@ -100,12 +105,14 @@ static inline int mem_sharing_gref_to_gf
return -EINVAL;
}
-static inline int gnttab_map_frame(struct domain *d, unsigned long idx,
- gfn_t gfn, mfn_t *mfn)
+static inline int gnttab_map_frame_begin(struct domain *d, unsigned long idx,
+ gfn_t gfn, mfn_t *mfn)
{
return -EINVAL;
}
+static inline void gnttab_map_frame_end(struct domain *d, mfn_t mfn) {}
+
static inline unsigned int gnttab_resource_max_frames(
const struct domain *d, unsigned int id)
{