[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.

Signed-off-by: Tu Dinh <ngoc-tu.dinh@xxxxxxxxxx>
---
 include/xen.h                     |   8 --
 src/xen/amd64/hypercall_thunk.asm | 101 +++++++++++-----
 src/xen/driver.c                  |  33 +++---
 src/xen/hypercall.c               | 191 +++++++++++++++++-------------
 src/xen/hypercall.h               |   2 +-
 src/xen/i386/hypercall_thunk.asm  |  69 ++++++++---
 src/xenbus/fdo.c                  |   2 -
 src/xenbus/suspend.c              |   2 -
 8 files changed, 254 insertions(+), 154 deletions(-)

diff --git a/include/xen.h b/include/xen.h
index 8691a0f..0c3e8a4 100644
--- a/include/xen.h
+++ b/include/xen.h
@@ -76,14 +76,6 @@ XenTouch(
     _In_ ULONG      BuildNumber
     );
 
-// HYPERCALL
-
-XEN_API
-VOID
-HypercallPopulate(
-    VOID
-    );
-
 // HVM
 
 _Check_return_
diff --git a/src/xen/amd64/hypercall_thunk.asm 
b/src/xen/amd64/hypercall_thunk.asm
index af045bb..b32ee2e 100644
--- a/src/xen/amd64/hypercall_thunk.asm
+++ b/src/xen/amd64/hypercall_thunk.asm
@@ -3,39 +3,76 @@
 
                         .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 hypercall2(uint32_t ord, 
uintptr_t arg1, uintptr_t arg2);
-                        public hypercall2
-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
-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 hypercall3(uint32_t ord, 
uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
-                        public hypercall3
-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
-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
+
+                        ; 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/xen/driver.c b/src/xen/driver.c
index 7b2621b..0bed6d1 100644
--- a/src/xen/driver.c
+++ b/src/xen/driver.c
@@ -621,31 +621,33 @@ DllInitialize(
 
     __DriverSetMemoryKey(MemoryKey);
 
-    HypercallInitialize();
+    status = HypercallInitialize();
+    if (!NT_SUCCESS(status))
+        goto fail8;
 
     status = AcpiInitialize();
     if (!NT_SUCCESS(status))
-        goto fail8;
+        goto fail9;
 
     status = SystemInitialize();
     if (!NT_SUCCESS(status))
-        goto fail9;
+        goto fail10;
 
     status = BugCheckInitialize();
     if (!NT_SUCCESS(status))
-        goto fail10;
+        goto fail11;
 
     status = ModuleInitialize();
     if (!NT_SUCCESS(status))
-        goto fail11;
+        goto fail12;
 
     status = ProcessInitialize();
     if (!NT_SUCCESS(status))
-        goto fail12;
+        goto fail13;
 
     status = UnplugInitialize();
     if (!NT_SUCCESS(status))
-        goto fail13;
+        goto fail14;
 
     RegistryCloseKey(ServiceKey);
 
@@ -653,36 +655,39 @@ DllInitialize(
 
     return STATUS_SUCCESS;
 
+fail14:
+    Error("fail14\n");
+
+    ProcessTeardown();
+
 fail13:
     Error("fail13\n");
 
-    ProcessTeardown();
+    ModuleTeardown();
 
 fail12:
     Error("fail12\n");
 
-    ModuleTeardown();
+    BugCheckTeardown();
 
 fail11:
     Error("fail11\n");
 
-    BugCheckTeardown();
+    SystemTeardown();
 
 fail10:
     Error("fail10\n");
 
-    SystemTeardown();
+    AcpiTeardown();
 
 fail9:
     Error("fail9\n");
 
-    AcpiTeardown();
+    HypercallTeardown();
 
 fail8:
     Error("fail8\n");
 
-    HypercallTeardown();
-
     RegistryCloseKey(MemoryKey);
     __DriverSetMemoryKey(NULL);
 
diff --git a/src/xen/hypercall.c b/src/xen/hypercall.c
index 911ae4f..3c68709 100644
--- a/src/xen/hypercall.c
+++ b/src/xen/hypercall.c
@@ -30,9 +30,6 @@
  * SUCH DAMAGE.
  */
 
-#undef  XEN_API
-#define XEN_API __declspec(dllexport)
-
 #include <ntddk.h>
 #include <xen.h>
 #include <intrin.h>
@@ -42,55 +39,63 @@
 #include "assert.h"
 #include "util.h"
 
-#define MAXIMUM_HYPERCALL_PAGE_COUNT 2
-
-#pragma code_seg("hypercall")
-__declspec(allocate("hypercall"))
-static UCHAR        __Section[(MAXIMUM_HYPERCALL_PAGE_COUNT + 1) * PAGE_SIZE];
-
-static ULONG        XenBaseLeaf = 0x40000000;
-
-static PHYSICAL_ADDRESS HypercallPage[MAXIMUM_HYPERCALL_PAGE_COUNT];
-static ULONG            HypercallPageCount;
-static BOOLEAN          HypercallPageInitialized;
-
-typedef UCHAR           HYPERCALL_GATE[32];
-typedef HYPERCALL_GATE  *PHYPERCALL_GATE;
-
-PHYPERCALL_GATE     Hypercall;
-ULONG               HypercallMsr;
-
-XEN_API
-VOID
-HypercallPopulate(
-    VOID
-    )
-{
-    ULONG       Index;
-
-    for (Index = 0; Index < HypercallPageCount; Index++) {
-        LogPrintf(LOG_LEVEL_INFO,
-                  "XEN: HYPERCALL PAGE %d @ %08x.%08x\n",
-                  Index,
-                  HypercallPage[Index].HighPart,
-                  HypercallPage[Index].LowPart);
-
-        __writemsr(HypercallMsr, HypercallPage[Index].QuadPart);
-    }
-
-    HypercallPageInitialized = TRUE;
-}
-
-VOID
+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;
+
+NTSTATUS
 HypercallInitialize(
     VOID
     )
 {
-    ULONG       EAX = 'DEAD';
-    ULONG       EBX = 'DEAD';
-    ULONG       ECX = 'DEAD';
-    ULONG       EDX = 'DEAD';
-    ULONG_PTR   Index;
+    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};
@@ -103,13 +108,13 @@ HypercallInitialize(
         if (strcmp(Signature, "XenVMMXenVMM") == 0 &&
             EAX >= XenBaseLeaf + 2)
             break;
-            
+
         XenBaseLeaf += 0x100;
-        
+
         if (XenBaseLeaf > 0x40000100) {
             LogPrintf(LOG_LEVEL_INFO,
                       "XEN: BASE CPUID LEAF NOT FOUND\n");
-            return;
+            return STATUS_NOT_SUPPORTED;
         }
     }
 
@@ -117,27 +122,46 @@ HypercallInitialize(
               "XEN: BASE CPUID LEAF @ %08x\n",
               XenBaseLeaf);
 
-    if ((ULONG_PTR)__Section & (PAGE_SIZE - 1))
-        Hypercall = (PVOID)(((ULONG_PTR)__Section + PAGE_SIZE - 1) & 
~(PAGE_SIZE - 1));
-    else
-        Hypercall = (PVOID)__Section;
-
-    ASSERT3U(((ULONG_PTR)Hypercall & (PAGE_SIZE - 1)), ==, 0);
+    __CpuId(0, &EAX, &EBX, &ECX, &EDX);
+    for (Index = 0; Index < ARRAYSIZE(HypercallVendorData); Index++) {
+        const CPU_VENDOR_DATA   *CurrentData = &HypercallVendorData[Index];
 
-    for (Index = 0; Index < MAXIMUM_HYPERCALL_PAGE_COUNT; Index++)
-        HypercallPage[Index] = MmGetPhysicalAddress((PUCHAR)Hypercall +
-                                                    (Index << PAGE_SHIFT));
+        if (EBX == CurrentData->EBX &&
+            ECX == CurrentData->ECX &&
+            EDX == CurrentData->EDX) {
+            Instruction = CurrentData->Instruction;
+            break;
+        }
+    }
 
-    __CpuId(XenBaseLeaf + 2, &EAX, &EBX, NULL, NULL);
-    HypercallPageCount = EAX;
-    ASSERT(HypercallPageCount <= MAXIMUM_HYPERCALL_PAGE_COUNT);
-    HypercallMsr = EBX;
+    if (Instruction == HYPERCALL_INSTRUCTION_UNKNOWN) {
+        LogPrintf(LOG_LEVEL_INFO,
+                  "XEN: CANNOT DETECT HYPERCALL INSTRUCTION\n");
+        return STATUS_NOT_SUPPORTED;
+    }
 
-    HypercallPopulate();
+    HypercallInstruction = Instruction;
+    return STATUS_SUCCESS;
 }
 
-extern uintptr_t __stdcall hypercall2(uint32_t ord, uintptr_t arg1, uintptr_t 
arg2);
-extern uintptr_t __stdcall hypercall3(uint32_t ord, uintptr_t arg1, uintptr_t 
arg2, uintptr_t arg3);
+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);
 
 LONG_PTR
 __Hypercall(
@@ -149,9 +173,6 @@ __Hypercall(
     va_list     Arguments;
     ULONG_PTR   Value;
 
-    if (!HypercallPageInitialized)
-        return -ENOSYS;
-
     va_start(Arguments, Count);
     switch (Count) {
     case 2: {
@@ -159,7 +180,16 @@ __Hypercall(
         uintptr_t  arg1 = va_arg(Arguments, ULONG_PTR);
         uintptr_t  arg2 = va_arg(Arguments, ULONG_PTR);
 
-        Value = hypercall2(ord, arg1, arg2);
+        switch (HypercallInstruction) {
+        case HYPERCALL_INSTRUCTION_VMCALL:
+            Value = hypercall2_vmcall(ord, arg1, arg2);
+            break;
+        case HYPERCALL_INSTRUCTION_VMMCALL:
+            Value = hypercall2_vmmcall(ord, arg1, arg2);
+            break;
+        default:
+            BUG("NO HYPERCALL INSTRUCTION");
+        }
         break;
     }
     case 3: {
@@ -168,7 +198,16 @@ __Hypercall(
         uintptr_t  arg2 = va_arg(Arguments, ULONG_PTR);
         uintptr_t  arg3 = va_arg(Arguments, ULONG_PTR);
 
-        Value = hypercall3(ord, arg1, arg2, arg3);
+        switch (HypercallInstruction) {
+        case HYPERCALL_INSTRUCTION_VMCALL:
+            Value = hypercall3_vmcall(ord, arg1, arg2, arg3);
+            break;
+        case HYPERCALL_INSTRUCTION_VMMCALL:
+            Value = hypercall3_vmmcall(ord, arg1, arg2, arg3);
+            break;
+        default:
+            BUG("NO HYPERCALL INSTRUCTION");
+        }
         break;
     }
     default:
@@ -185,12 +224,4 @@ HypercallTeardown(
     VOID
     )
 {
-    ULONG   Index;
-
-    Hypercall = NULL;
-
-    for (Index = 0; Index < MAXIMUM_HYPERCALL_PAGE_COUNT; Index++)
-        HypercallPage[Index].QuadPart = 0;
-
-    HypercallPageCount = 0;
 }
diff --git a/src/xen/hypercall.h b/src/xen/hypercall.h
index 306d9ab..6f4c7e0 100644
--- a/src/xen/hypercall.h
+++ b/src/xen/hypercall.h
@@ -40,7 +40,7 @@
 
 #include <public/xen.h>
 
-extern VOID
+extern NTSTATUS
 HypercallInitialize(
     VOID
     );
diff --git a/src/xen/i386/hypercall_thunk.asm b/src/xen/i386/hypercall_thunk.asm
index a15ae27..be68db3 100644
--- a/src/xen/i386/hypercall_thunk.asm
+++ b/src/xen/i386/hypercall_thunk.asm
@@ -5,28 +5,69 @@
                         .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 hypercall2(uint32_t ord, 
uintptr_t arg1, uintptr_t arg2);
-                        public _hypercall2@12
-_hypercall2@12         proc
+                        ; 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
-                        shl     eax, 5
-                        add     eax, dword ptr [_Hypercall]
-                        call    eax
+                        vmmcall
                         pop     ebx
                         leave
                         ret     0Ch
-_hypercall2@12         endp
+_hypercall2_vmmcall@12  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
 
-                        ; uintptr_t __stdcall hypercall3(uint32_t ord, 
uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
-                        public _hypercall3@16
-_hypercall3@16         proc
+                        ; 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
@@ -34,12 +75,10 @@ _hypercall3@16      proc
                         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
+                        vmmcall
                         pop     ebx
                         leave
                         ret     10h
-_hypercall3@16         endp
+_hypercall3_vmmcall@16  endp
 
                         end
diff --git a/src/xenbus/fdo.c b/src/xenbus/fdo.c
index 8695a56..a5a0ca1 100644
--- a/src/xenbus/fdo.c
+++ b/src/xenbus/fdo.c
@@ -3831,8 +3831,6 @@ FdoS4ToS3(
 
     LogResume();
 
-    HypercallPopulate();
-
     UnplugDevices();
 
 not_active:
diff --git a/src/xenbus/suspend.c b/src/xenbus/suspend.c
index cd292c0..687f86c 100644
--- a/src/xenbus/suspend.c
+++ b/src/xenbus/suspend.c
@@ -205,8 +205,6 @@ SuspendEarly(
     if (Cpu != 0)
         return;
 
-    HypercallPopulate();
-
     UnplugDevices();
 
     for (ListEntry = Context->EarlyList.Flink;
-- 
2.51.2.windows.1



--
Ngoc Tu Dinh | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech




 


Rackspace

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