[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Use direct hypercall instead of hypercall page



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>
---
 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
-- 
2.51.2.windows.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.