x86/sev: Harden #VC instruction emulation somewhat
authorBorislav Petkov (AMD) <bp@alien8.de>
Fri, 5 Jan 2024 10:14:07 +0000 (11:14 +0100)
committerBorislav Petkov (AMD) <bp@alien8.de>
Mon, 29 Jan 2024 16:08:22 +0000 (17:08 +0100)
Compare the opcode bytes at rIP for each #VC exit reason to verify the
instruction which raised the #VC exception is actually the right one.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Link: https://lore.kernel.org/r/20240105101407.11694-1-bp@alien8.de
arch/x86/boot/compressed/sev.c
arch/x86/kernel/sev-shared.c
arch/x86/kernel/sev.c

index 454acd7a2dafff2a0d16cc2a0ddd61745b3fcf36..073291832f44d27096984c0dd236039d0d955cd3 100644 (file)
@@ -304,6 +304,10 @@ void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
        if (result != ES_OK)
                goto finish;
 
+       result = vc_check_opcode_bytes(&ctxt, exit_code);
+       if (result != ES_OK)
+               goto finish;
+
        switch (exit_code) {
        case SVM_EXIT_RDTSC:
        case SVM_EXIT_RDTSCP:
index 1d24ec6799157be4080adc903b38394f8538b35b..5db24d0fc557cde9efb7b99cc90e01eed6516696 100644 (file)
  */
 
 #ifndef __BOOT_COMPRESSED
-#define error(v)       pr_err(v)
-#define has_cpuflag(f) boot_cpu_has(f)
+#define error(v)                       pr_err(v)
+#define has_cpuflag(f)                 boot_cpu_has(f)
+#define sev_printk(fmt, ...)           printk(fmt, ##__VA_ARGS__)
+#define sev_printk_rtl(fmt, ...)       printk_ratelimited(fmt, ##__VA_ARGS__)
 #else
 #undef WARN
 #define WARN(condition, format...) (!!(condition))
+#define sev_printk(fmt, ...)
+#define sev_printk_rtl(fmt, ...)
 #endif
 
 /* I/O parameters for CPUID-related helpers */
@@ -574,6 +578,7 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code)
 {
        unsigned int subfn = lower_bits(regs->cx, 32);
        unsigned int fn = lower_bits(regs->ax, 32);
+       u16 opcode = *(unsigned short *)regs->ip;
        struct cpuid_leaf leaf;
        int ret;
 
@@ -581,6 +586,10 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code)
        if (exit_code != SVM_EXIT_CPUID)
                goto fail;
 
+       /* Is it really a CPUID insn? */
+       if (opcode != 0xa20f)
+               goto fail;
+
        leaf.fn = fn;
        leaf.subfn = subfn;
 
@@ -1170,3 +1179,92 @@ static int vmgexit_psc(struct ghcb *ghcb, struct snp_psc_desc *desc)
 out:
        return ret;
 }
+
+static enum es_result vc_check_opcode_bytes(struct es_em_ctxt *ctxt,
+                                           unsigned long exit_code)
+{
+       unsigned int opcode = (unsigned int)ctxt->insn.opcode.value;
+       u8 modrm = ctxt->insn.modrm.value;
+
+       switch (exit_code) {
+
+       case SVM_EXIT_IOIO:
+       case SVM_EXIT_NPF:
+               /* handled separately */
+               return ES_OK;
+
+       case SVM_EXIT_CPUID:
+               if (opcode == 0xa20f)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_INVD:
+               if (opcode == 0x080f)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_MONITOR:
+               if (opcode == 0x010f && modrm == 0xc8)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_MWAIT:
+               if (opcode == 0x010f && modrm == 0xc9)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_MSR:
+               /* RDMSR */
+               if (opcode == 0x320f ||
+               /* WRMSR */
+                   opcode == 0x300f)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_RDPMC:
+               if (opcode == 0x330f)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_RDTSC:
+               if (opcode == 0x310f)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_RDTSCP:
+               if (opcode == 0x010f && modrm == 0xf9)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_READ_DR7:
+               if (opcode == 0x210f &&
+                   X86_MODRM_REG(ctxt->insn.modrm.value) == 7)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_VMMCALL:
+               if (opcode == 0x010f && modrm == 0xd9)
+                       return ES_OK;
+
+               break;
+
+       case SVM_EXIT_WRITE_DR7:
+               if (opcode == 0x230f &&
+                   X86_MODRM_REG(ctxt->insn.modrm.value) == 7)
+                       return ES_OK;
+               break;
+
+       case SVM_EXIT_WBINVD:
+               if (opcode == 0x90f)
+                       return ES_OK;
+               break;
+
+       default:
+               break;
+       }
+
+       sev_printk(KERN_ERR "Wrong/unhandled opcode bytes: 0x%x, exit_code: 0x%lx, rIP: 0x%lx\n",
+                  opcode, exit_code, ctxt->regs->ip);
+
+       return ES_UNSUPPORTED;
+}
index c67285824e82676528ab8f33e2919bc021b197d2..1ec753331524abb6847ac90f8e9bea912f42c7f0 100644 (file)
@@ -1752,7 +1752,10 @@ static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt,
                                         struct ghcb *ghcb,
                                         unsigned long exit_code)
 {
-       enum es_result result;
+       enum es_result result = vc_check_opcode_bytes(ctxt, exit_code);
+
+       if (result != ES_OK)
+               return result;
 
        switch (exit_code) {
        case SVM_EXIT_READ_DR7: