|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH] Use direct hypercall instead of hypercall page
On 24/04/2026 11:17, Owen Smith wrote:
> XSA-466 "Xen hypercall page unsafe against speculative attacks"
> recommends that OSes avoid using the hypercall page, since that breaks
> the use of return thunks and CET IBT.
>
> While Windows doesn't support return thunks or CET IBT, the current
> hypercall code uses a naked indirect call to call into the hypercall
> page. This call is not protected by Windows CFG, and cannot be patched
> by Windows's Retpoline implementation.
>
> Convert the hypercall code to detect CPU vendor and use the appropriate
> hypercall instructions.
>
> Ported from xenbus 2635e7c08ee8df3e3c1be427926a53fe1b345c96
> by Tu Dinh <ngoc-tu.dinh@xxxxxxxxxx>
>
> Signed-off-by: Owen Smith <owen.smith@xxxxxxxxxx>
Reviewed-by: Tu Dinh <ngoc-tu.dinh@xxxxxxxxxx>
Thanks,
> ---
> src/xencrsh/amd64/hypercall.asm | 109 ++++++++++++------
> src/xencrsh/hvm.c | 120 +-------------------
> src/xencrsh/hypercall.h | 6 +-
> src/xencrsh/hypercall_stub.c | 191 +++++++++++++++++++++++++++++++-
> src/xencrsh/i386/hypercall.asm | 119 +++++++++++++-------
> 5 files changed, 342 insertions(+), 203 deletions(-)
>
> diff --git a/src/xencrsh/amd64/hypercall.asm b/src/xencrsh/amd64/hypercall.asm
> index 1ceda85..de430e7 100644
> --- a/src/xencrsh/amd64/hypercall.asm
> +++ b/src/xencrsh/amd64/hypercall.asm
> @@ -1,41 +1,78 @@
> - page ,132
> - title Hypercall Gates
> + page ,132
> + title Hypercall Thunks
>
> - .code
> + .code
>
> - extrn Hypercall:qword
> + ; uintptr_t __stdcall hypercall2_vmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2);
> + public hypercall2_vmcall
> +hypercall2_vmcall proc
> + push rdi
> + push rsi
> + mov eax, ecx ; ord
> + mov rdi, rdx ; arg1
> + mov rsi, r8 ; arg2
> + vmcall
> + pop rsi
> + pop rdi
> + ret
> +hypercall2_vmcall endp
>
> - ; uintptr_t __stdcall asm___hypercall2(uint32_t ord,
> uintptr_t arg1, uintptr_t arg2);
> - public asm___hypercall2
> -asm___hypercall2 proc
> - push rdi
> - push rsi
> - mov rdi, rdx ; arg1
> - mov rax, qword ptr [Hypercall]
> - shl rcx, 5 ; ord
> - add rax, rcx
> - mov rsi, r8 ; arg2
> - call rax
> - pop rsi
> - pop rdi
> - ret
> -asm___hypercall2 endp
> + ; uintptr_t __stdcall hypercall2_vmmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2);
> + public hypercall2_vmmcall
> +hypercall2_vmmcall proc
> + push rdi
> + push rsi
> + mov eax, ecx ; ord
> + mov rdi, rdx ; arg1
> + mov rsi, r8 ; arg2
> + vmmcall
> + pop rsi
> + pop rdi
> + ret
> +hypercall2_vmmcall endp
>
> - ; uintptr_t __stdcall asm___hypercall3(uint32_t ord,
> uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
> - public asm___hypercall3
> -asm___hypercall3 proc
> - push rdi
> - push rsi
> - mov rdi, rdx ; arg1
> - mov rax, qword ptr [Hypercall]
> - shl rcx, 5 ; ord
> - add rax, rcx
> - mov rsi, r8 ; arg2
> - mov rdx, r9 ; arg3
> - call rax
> - pop rsi
> - pop rdi
> - ret
> -asm___hypercall3 endp
> + ; uintptr_t __stdcall hypercall3_vmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2,
> + ; uintptr_t arg3);
> + public hypercall3_vmcall
> +hypercall3_vmcall proc
> + push rdi
> + push rsi
> + mov eax, ecx ; ord
> + mov rdi, rdx ; arg1
> + mov rsi, r8 ; arg2
> + mov rdx, r9 ; arg3
> + vmcall
> + pop rsi
> + pop rdi
> + ret
> +hypercall3_vmcall endp
>
> - end
> + ; uintptr_t __stdcall hypercall3_vmmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2,
> + ; uintptr_t arg3);
> + public hypercall3_vmmcall
> +hypercall3_vmmcall proc
> + push rdi
> + push rsi
> + mov eax, ecx ; ord
> + mov rdi, rdx ; arg1
> + mov rsi, r8 ; arg2
> + mov rdx, r9 ; arg3
> + vmmcall
> + pop rsi
> + pop rdi
> + ret
> +hypercall3_vmmcall endp
> +
> + end
> diff --git a/src/xencrsh/hvm.c b/src/xencrsh/hvm.c
> index 04da4e3..82230ef 100644
> --- a/src/xencrsh/hvm.c
> +++ b/src/xencrsh/hvm.c
> @@ -50,22 +50,6 @@
> #include "log.h"
> #include "assert.h"
>
> -#define MAXIMUM_HYPERCALL_PFN_COUNT 2
> -
> -#pragma code_seg("hypercall")
> -__declspec(allocate("hypercall"))
> -static UCHAR __HypercallSection[(MAXIMUM_HYPERCALL_PFN_COUNT + 1) *
> PAGE_SIZE];
> -
> -#define XEN_SIGNATURE "XenVMMXenVMM"
> -
> -static ULONG __BaseLeaf = 0x40000000;
> -static USHORT __MajorVersion;
> -static USHORT __MinorVersion;
> -static PFN_NUMBER __Pfn[MAXIMUM_HYPERCALL_PFN_COUNT];
> -static ULONG __PfnCount;
> -
> -PHYPERCALL_GATE Hypercall;
> -
> //#pragma code_seg("sharedinfo")
> //__declspec(allocate("sharedinfo"))
> //static UCHAR __SharedInfoSection[(1 + 1) * PAGE_SIZE];
> @@ -95,102 +79,6 @@ fail:
> return Status;
> }
>
> -static FORCEINLINE VOID
> -CpuId(
> - IN ULONG Leaf,
> - OUT PULONG EAX OPTIONAL,
> - OUT PULONG EBX OPTIONAL,
> - OUT PULONG ECX OPTIONAL,
> - OUT PULONG EDX OPTIONAL
> - )
> -{
> - int Value[4] = {0};
> -
> - __cpuid(Value, Leaf);
> -
> - if (EAX)
> - *EAX = (ULONG)Value[0];
> -
> - if (EBX)
> - *EBX = (ULONG)Value[1];
> -
> - if (ECX)
> - *ECX = (ULONG)Value[2];
> -
> - if (EDX)
> - *EDX = (ULONG)Value[3];
> -}
> -
> -static FORCEINLINE NTSTATUS
> -__InitHypercallPage()
> -{
> - ULONG eax = 'DEAD';
> - ULONG ebx = 'DEAD';
> - ULONG ecx = 'DEAD';
> - ULONG edx = 'DEAD';
> - ULONG_PTR Index;
> - ULONG HypercallMsr;
> -
> - NTSTATUS Status;
> -
> - Status = STATUS_UNSUCCESSFUL;
> - for (;;) {
> - CHAR Signature[13] = { 0 };
> -
> - CpuId(__BaseLeaf, &eax, &ebx, &ecx, &edx);
> - *((PULONG)(Signature + 0)) = ebx;
> - *((PULONG)(Signature + 4)) = ecx;
> - *((PULONG)(Signature + 8)) = edx;
> -
> - if (strcmp(Signature, XEN_SIGNATURE) == 0 &&
> - eax >= __BaseLeaf + 2)
> - break;
> -
> - __BaseLeaf += 0x100;
> -
> - if (__BaseLeaf > 0x40000100)
> - goto fail1;
> - }
> -
> - CpuId(__BaseLeaf + 1, &eax, NULL, NULL, NULL);
> - __MajorVersion = (USHORT)(eax >> 16);
> - __MinorVersion = (USHORT)(eax & 0xFFFF);
> -
> - LogVerbose("XEN %d.%d\n", __MajorVersion, __MinorVersion);
> - LogVerbose("INTERFACE 0x%08x\n", __XEN_INTERFACE_VERSION__);
> -
> - if ((ULONG_PTR)__HypercallSection & (PAGE_SIZE - 1))
> - Hypercall = (PVOID)(((ULONG_PTR)__HypercallSection + PAGE_SIZE - 1)
> & ~(PAGE_SIZE - 1));
> - else
> - Hypercall = (PVOID)__HypercallSection;
> -
> - ASSERT3U(((ULONG_PTR)Hypercall & (PAGE_SIZE - 1)), ==, 0);
> -
> - for (Index = 0; Index < MAXIMUM_HYPERCALL_PFN_COUNT; Index++) {
> - PHYSICAL_ADDRESS PhysicalAddress;
> -
> - PhysicalAddress = MmGetPhysicalAddress((PUCHAR)Hypercall + (Index <<
> PAGE_SHIFT));
> - __Pfn[Index] = (PFN_NUMBER)(PhysicalAddress.QuadPart >> PAGE_SHIFT);
> - }
> -
> - CpuId(__BaseLeaf + 2, &eax, &ebx, NULL, NULL);
> - __PfnCount = eax;
> - ASSERT(__PfnCount <= MAXIMUM_HYPERCALL_PFN_COUNT);
> - HypercallMsr = ebx;
> -
> - for (Index = 0; Index < __PfnCount; Index++) {
> - LogVerbose("HypercallPfn[%d]: %p\n", Index, (PVOID)__Pfn[Index]);
> - __writemsr(HypercallMsr, (ULONG64)__Pfn[Index] << PAGE_SHIFT);
> - }
> -
> - return STATUS_SUCCESS;
> -
> -fail1:
> - LogError("fail1 (%08x)", Status);
> -
> - return Status;
> -}
> -
> //static NTSTATUS
> //__InitSharedInfo()
> //{
> @@ -235,7 +123,7 @@ HvmInitialize()
> {
> NTSTATUS Status;
>
> - Status = __InitHypercallPage();
> + Status = HypercallInitialize();
> if (!NT_SUCCESS(Status))
> goto fail;
>
> @@ -258,12 +146,6 @@ fail:
> VOID
> HvmTerminate()
> {
> - ULONG Index;
> -
> - Hypercall = NULL;
> - for (Index = 0; Index < MAXIMUM_HYPERCALL_PFN_COUNT; ++Index) {
> - __Pfn[Index] = 0;
> - }
> //SharedInfo = NULL;
> }
>
> diff --git a/src/xencrsh/hypercall.h b/src/xencrsh/hypercall.h
> index 762ecf8..6b055fa 100644
> --- a/src/xencrsh/hypercall.h
> +++ b/src/xencrsh/hypercall.h
> @@ -35,8 +35,10 @@
>
> #include "driver.h"
>
> -typedef UCHAR HYPERCALL_GATE[32];
> -typedef HYPERCALL_GATE *PHYPERCALL_GATE;
> +extern NTSTATUS
> +HypercallInitialize(
> + VOID
> + );
>
> extern ULONG_PTR
> ___Hypercall2(
> diff --git a/src/xencrsh/hypercall_stub.c b/src/xencrsh/hypercall_stub.c
> index 1741850..5183f58 100644
> --- a/src/xencrsh/hypercall_stub.c
> +++ b/src/xencrsh/hypercall_stub.c
> @@ -32,16 +32,164 @@
>
> #include <wdm.h>
> #include <xenvbd-storport.h>
> -
> +#include <intrin.h>
>
> #include <xen-version.h>
> #include <xen\xen-compat.h>
>
> #include <xen-types.h>
>
> +#include "log.h"
> +#include "assert.h"
> +
> #include "hypercall.h"
>
> -extern uintptr_t __stdcall asm___hypercall2(uint32_t ord, uintptr_t arg1,
> uintptr_t arg2);
> +typedef enum _HYPERCALL_INSTRUCTION {
> + HYPERCALL_INSTRUCTION_UNKNOWN,
> + HYPERCALL_INSTRUCTION_VMCALL,
> + HYPERCALL_INSTRUCTION_VMMCALL,
> +} HYPERCALL_INSTRUCTION;
> +
> +typedef struct _CPU_VENDOR_DATA {
> + ULONG EBX;
> + ULONG ECX;
> + ULONG EDX;
> + HYPERCALL_INSTRUCTION Instruction;
> +} CPU_VENDOR_DATA;
> +
> +static const CPU_VENDOR_DATA HypercallVendorData[] = {
> + // Note that the vendor data goes EBX-ECX-EDX
> + {
> + // "GenuineIntel"
> + 0x756E6547, 0x6C65746E, 0x49656E69,
> + HYPERCALL_INSTRUCTION_VMCALL
> + },
> + {
> + // "AuthenticAMD"
> + 0x68747541, 0x444D4163, 0x69746E65,
> + HYPERCALL_INSTRUCTION_VMMCALL
> + },
> + {
> + // "CentaurHauls"
> + 0x746E6543, 0x736C7561, 0x48727561,
> + HYPERCALL_INSTRUCTION_VMCALL
> + },
> + {
> + // " Shanghai "
> + 0x68532020, 0x20206961, 0x68676E61,
> + HYPERCALL_INSTRUCTION_VMCALL
> + },
> + {
> + // "HygonGenuine"
> + 0x6F677948, 0x656E6975, 0x6E65476E,
> + HYPERCALL_INSTRUCTION_VMMCALL
> + },
> +};
> +
> +static HYPERCALL_INSTRUCTION HypercallInstruction
> + = HYPERCALL_INSTRUCTION_UNKNOWN;
> +
> +static FORCEINLINE VOID
> +__CpuId(
> + IN ULONG Leaf,
> + OUT PULONG EAX OPTIONAL,
> + OUT PULONG EBX OPTIONAL,
> + OUT PULONG ECX OPTIONAL,
> + OUT PULONG EDX OPTIONAL
> + )
> +{
> + int Value[4] = {0};
> +
> + __cpuid(Value, Leaf);
> +
> + if (EAX)
> + *EAX = (ULONG)Value[0];
> +
> + if (EBX)
> + *EBX = (ULONG)Value[1];
> +
> + if (ECX)
> + *ECX = (ULONG)Value[2];
> +
> + if (EDX)
> + *EDX = (ULONG)Value[3];
> +}
> +
> +NTSTATUS
> +HypercallInitialize(
> + VOID
> + )
> +{
> + ULONG XenBaseLeaf = 0x40000000;
> + ULONG EAX = 'DEAD';
> + ULONG EBX = 'DEAD';
> + ULONG ECX = 'DEAD';
> + ULONG EDX = 'DEAD';
> + HYPERCALL_INSTRUCTION Instruction = HYPERCALL_INSTRUCTION_UNKNOWN;
> + ULONG Index;
> +
> + for (;;) {
> + CHAR Signature[13] = {0};
> +
> + __CpuId(XenBaseLeaf, &EAX, &EBX, &ECX, &EDX);
> + *((PULONG)(Signature + 0)) = EBX;
> + *((PULONG)(Signature + 4)) = ECX;
> + *((PULONG)(Signature + 8)) = EDX;
> +
> + if (strcmp(Signature, "XenVMMXenVMM") == 0 &&
> + EAX >= XenBaseLeaf + 2)
> + break;
> +
> + XenBaseLeaf += 0x100;
> +
> + if (XenBaseLeaf > 0x40000100) {
> + LogVerbose("XEN: BASE CPUID LEAF NOT FOUND\n");
> + return STATUS_NOT_SUPPORTED;
> + }
> + }
> +
> + LogVerbose("XEN: BASE CPUID LEAF @ %08x\n",
> + XenBaseLeaf);
> +
> + __CpuId(0, &EAX, &EBX, &ECX, &EDX);
> + for (Index = 0; Index < ARRAYSIZE(HypercallVendorData); Index++) {
> + const CPU_VENDOR_DATA *CurrentData = &HypercallVendorData[Index];
> +
> + if (EBX == CurrentData->EBX &&
> + ECX == CurrentData->ECX &&
> + EDX == CurrentData->EDX) {
> + Instruction = CurrentData->Instruction;
> + break;
> + }
> + }
> +
> + if (Instruction == HYPERCALL_INSTRUCTION_UNKNOWN) {
> + LogVerbose("XEN: CANNOT DETECT HYPERCALL INSTRUCTION\n");
> + return STATUS_NOT_SUPPORTED;
> + }
> +
> + HypercallInstruction = Instruction;
> + return STATUS_SUCCESS;
> +}
> +
> +extern uintptr_t __stdcall hypercall2_vmcall(
> + uint32_t ord,
> + uintptr_t arg1,
> + uintptr_t arg2);
> +extern uintptr_t __stdcall hypercall2_vmmcall(
> + uint32_t ord,
> + uintptr_t arg1,
> + uintptr_t arg2);
> +extern uintptr_t __stdcall hypercall3_vmcall(
> + uint32_t ord,
> + uintptr_t arg1,
> + uintptr_t arg2,
> + uintptr_t arg3);
> +extern uintptr_t __stdcall hypercall3_vmmcall(
> + uint32_t ord,
> + uintptr_t arg1,
> + uintptr_t arg2,
> + uintptr_t arg3);
>
> ULONG_PTR
> ___Hypercall2(
> @@ -50,10 +198,24 @@ ___Hypercall2(
> ULONG_PTR Argument2
> )
> {
> - return asm___hypercall2(Ordinal, Argument1, Argument2);
> -}
> + ULONG_PTR Value;
>
> -extern uintptr_t __stdcall asm___hypercall3(uint32_t ord, uintptr_t arg1,
> uintptr_t arg2, uintptr_t arg3);
> + switch (HypercallInstruction) {
> + case HYPERCALL_INSTRUCTION_VMCALL:
> + Value = hypercall2_vmcall(Ordinal, Argument1, Argument2);
> + break;
> +
> + case HYPERCALL_INSTRUCTION_VMMCALL:
> + Value = hypercall2_vmmcall(Ordinal, Argument1, Argument2);
> + break;
> +
> + default:
> + Value = 0;
> + BUG("NO HYPERCALL INSTRUCTION");
> + }
> +
> + return Value;
> +}
>
> ULONG_PTR
> ___Hypercall3(
> @@ -63,5 +225,22 @@ ___Hypercall3(
> ULONG_PTR Argument3
> )
> {
> - return asm___hypercall3(Ordinal, Argument1, Argument2, Argument3);
> + ULONG_PTR Value;
> +
> + switch (HypercallInstruction) {
> + case HYPERCALL_INSTRUCTION_VMCALL:
> + Value = hypercall3_vmcall(Ordinal, Argument1, Argument2, Argument3);
> + break;
> +
> + case HYPERCALL_INSTRUCTION_VMMCALL:
> + Value = hypercall3_vmmcall(Ordinal, Argument1, Argument2, Argument3);
> + break;
> +
> + default:
> + Value = 0;
> + BUG("NO HYPERCALL INSTRUCTION");
> + }
> +
> + return Value;
> +
> }
> diff --git a/src/xencrsh/i386/hypercall.asm b/src/xencrsh/i386/hypercall.asm
> index 51094be..be68db3 100644
> --- a/src/xencrsh/i386/hypercall.asm
> +++ b/src/xencrsh/i386/hypercall.asm
> @@ -1,45 +1,84 @@
> - page ,132
> - title Hypercall Gates
> + page ,132
> + title Hypercall Thunks
>
> - .686p
> - .model FLAT
> - .code
> + .686p
> + .model FLAT
> + .code
>
> - extrn _Hypercall:dword
> + ; uintptr_t __stdcall hypercall2_vmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2);
> + public _hypercall2_vmcall@12
> +_hypercall2_vmcall@12 proc
> + push ebp
> + mov ebp, esp
> + push ebx
> + mov eax, [ebp + 08h] ; ord
> + mov ebx, [ebp + 0ch] ; arg1
> + mov ecx, [ebp + 10h] ; arg2
> + vmcall
> + pop ebx
> + leave
> + ret 0Ch
> +_hypercall2_vmcall@12 endp
>
> - ; uintptr_t __stdcall asm___hypercall2(uint32_t ord,
> uintptr_t arg1, uintptr_t arg2);
> - public _asm___hypercall2@12
> -_asm___hypercall2@12 proc
> - push ebp
> - mov ebp, esp
> - push ebx
> - mov eax, [ebp + 08h] ; ord
> - mov ebx, [ebp + 0ch] ; arg1
> - mov ecx, [ebp + 10h] ; arg2
> - shl eax, 5
> - add eax, dword ptr [_Hypercall]
> - call eax
> - pop ebx
> - leave
> - ret 0Ch
> -_asm___hypercall2@12 endp
> + ; uintptr_t __stdcall hypercall2_vmmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2);
> + public _hypercall2_vmmcall@12
> +_hypercall2_vmmcall@12 proc
> + push ebp
> + mov ebp, esp
> + push ebx
> + mov eax, [ebp + 08h] ; ord
> + mov ebx, [ebp + 0ch] ; arg1
> + mov ecx, [ebp + 10h] ; arg2
> + vmmcall
> + pop ebx
> + leave
> + ret 0Ch
> +_hypercall2_vmmcall@12 endp
>
> - ; uintptr_t __stdcall asm___hypercall3(uint32_t ord,
> uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
> - public _asm___hypercall3@16
> -_asm___hypercall3@16 proc
> - push ebp
> - mov ebp, esp
> - push ebx
> - mov eax, [ebp + 08h] ; ord
> - mov ebx, [ebp + 0ch] ; arg1
> - mov ecx, [ebp + 10h] ; arg2
> - mov edx, [ebp + 14h] ; arg3
> - shl eax, 5
> - add eax, dword ptr [_Hypercall]
> - call eax
> - pop ebx
> - leave
> - ret 10h
> -_asm___hypercall3@16 endp
> + ; uintptr_t __stdcall hypercall3_vmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2,
> + ; uintptr_t arg3);
> + public _hypercall3_vmcall@16
> +_hypercall3_vmcall@16 proc
> + push ebp
> + mov ebp, esp
> + push ebx
> + mov eax, [ebp + 08h] ; ord
> + mov ebx, [ebp + 0ch] ; arg1
> + mov ecx, [ebp + 10h] ; arg2
> + mov edx, [ebp + 14h] ; arg3
> + vmcall
> + pop ebx
> + leave
> + ret 10h
> +_hypercall3_vmcall@16 endp
>
> - end
> + ; uintptr_t __stdcall hypercall3_vmmcall(
> + ; uint32_t ord,
> + ; uintptr_t arg1,
> + ; uintptr_t arg2,
> + ; uintptr_t arg3);
> + public _hypercall3_vmmcall@16
> +_hypercall3_vmmcall@16 proc
> + push ebp
> + mov ebp, esp
> + push ebx
> + mov eax, [ebp + 08h] ; ord
> + mov ebx, [ebp + 0ch] ; arg1
> + mov ecx, [ebp + 10h] ; arg2
> + mov edx, [ebp + 14h] ; arg3
> + vmmcall
> + pop ebx
> + leave
> + ret 10h
> +_hypercall3_vmmcall@16 endp
> +
> + end
--
Ngoc Tu Dinh | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |