Skip to content

Commit

Permalink
x86/sev: Harden #VC instruction emulation somewhat
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bp3tk0v committed Jan 29, 2024
1 parent 41bccc9 commit e3ef461
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 3 deletions.
4 changes: 4 additions & 0 deletions arch/x86/boot/compressed/sev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
102 changes: 100 additions & 2 deletions arch/x86/kernel/sev-shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
*/

#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 */
Expand Down Expand Up @@ -574,13 +578,18 @@ 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;

/* Only CPUID is supported via MSR protocol */
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;

Expand Down Expand Up @@ -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;
}
5 changes: 4 additions & 1 deletion arch/x86/kernel/sev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit e3ef461

Please sign in to comment.