bhyve: intercept AMD SVM instructions.

Intercept and report #UD to VM on SVM/AMD in case VM tried to execute an
SVM instruction.  Otherwise, SVM allows execution of them, and instructions
operate on host physical addresses despite being executed in guest mode.

Reported by:	Maxime Villard <max@m00nbsd.net>
admbug:	972
CVE:	CVE-2020-7467
Reviewed by:	grehan, markj
Differential revision:	https://reviews.freebsd.org/D26313
This commit is contained in:
Konstantin Belousov 2020-09-15 20:22:50 +00:00
parent 448000279e
commit 101d5b527a
2 changed files with 74 additions and 36 deletions

View File

@ -488,10 +488,23 @@ vmcb_init(struct svm_softc *sc, int vcpu, uint64_t iopm_base_pa,
svm_enable_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_SHUTDOWN);
svm_enable_intercept(sc, vcpu, VMCB_CTRL1_INTCPT,
VMCB_INTCPT_FERR_FREEZE);
svm_enable_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_INVD);
svm_enable_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_INVLPGA);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_MONITOR);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_MWAIT);
/*
* Intercept SVM instructions since AMD enables them in guests otherwise.
* Non-intercepted VMMCALL causes #UD, skip it.
*/
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_VMLOAD);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_VMSAVE);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_STGI);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_CLGI);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_SKINIT);
svm_enable_intercept(sc, vcpu, VMCB_CTRL2_INTCPT, VMCB_INTCPT_ICEBP);
/*
* From section "Canonicalization and Consistency Checks" in APMv2
* the VMRUN intercept bit must be set to pass the consistency check.
@ -1236,43 +1249,45 @@ emulate_rdmsr(struct svm_softc *sc, int vcpu, u_int num, bool *retu)
static const char *
exit_reason_to_str(uint64_t reason)
{
int i;
static char reasonbuf[32];
static const struct {
int reason;
const char *str;
} reasons[] = {
{ .reason = VMCB_EXIT_INVALID, .str = "invalvmcb" },
{ .reason = VMCB_EXIT_SHUTDOWN, .str = "shutdown" },
{ .reason = VMCB_EXIT_NPF, .str = "nptfault" },
{ .reason = VMCB_EXIT_PAUSE, .str = "pause" },
{ .reason = VMCB_EXIT_HLT, .str = "hlt" },
{ .reason = VMCB_EXIT_CPUID, .str = "cpuid" },
{ .reason = VMCB_EXIT_IO, .str = "inout" },
{ .reason = VMCB_EXIT_MC, .str = "mchk" },
{ .reason = VMCB_EXIT_INTR, .str = "extintr" },
{ .reason = VMCB_EXIT_NMI, .str = "nmi" },
{ .reason = VMCB_EXIT_VINTR, .str = "vintr" },
{ .reason = VMCB_EXIT_MSR, .str = "msr" },
{ .reason = VMCB_EXIT_IRET, .str = "iret" },
{ .reason = VMCB_EXIT_MONITOR, .str = "monitor" },
{ .reason = VMCB_EXIT_MWAIT, .str = "mwait" },
{ .reason = VMCB_EXIT_VMRUN, .str = "vmrun" },
{ .reason = VMCB_EXIT_VMMCALL, .str = "vmmcall" },
{ .reason = VMCB_EXIT_VMLOAD, .str = "vmload" },
{ .reason = VMCB_EXIT_VMSAVE, .str = "vmsave" },
{ .reason = VMCB_EXIT_STGI, .str = "stgi" },
{ .reason = VMCB_EXIT_CLGI, .str = "clgi" },
{ .reason = VMCB_EXIT_SKINIT, .str = "skinit" },
{ .reason = VMCB_EXIT_ICEBP, .str = "icebp" },
{ .reason = VMCB_EXIT_INVD, .str = "invd" },
{ .reason = VMCB_EXIT_INVLPGA, .str = "invlpga" },
};
switch (reason) {
case VMCB_EXIT_INVALID:
return ("invalvmcb");
case VMCB_EXIT_SHUTDOWN:
return ("shutdown");
case VMCB_EXIT_NPF:
return ("nptfault");
case VMCB_EXIT_PAUSE:
return ("pause");
case VMCB_EXIT_HLT:
return ("hlt");
case VMCB_EXIT_CPUID:
return ("cpuid");
case VMCB_EXIT_IO:
return ("inout");
case VMCB_EXIT_MC:
return ("mchk");
case VMCB_EXIT_INTR:
return ("extintr");
case VMCB_EXIT_NMI:
return ("nmi");
case VMCB_EXIT_VINTR:
return ("vintr");
case VMCB_EXIT_MSR:
return ("msr");
case VMCB_EXIT_IRET:
return ("iret");
case VMCB_EXIT_MONITOR:
return ("monitor");
case VMCB_EXIT_MWAIT:
return ("mwait");
default:
snprintf(reasonbuf, sizeof(reasonbuf), "%#lx", reason);
return (reasonbuf);
for (i = 0; i < nitems(reasons); i++) {
if (reasons[i].reason == reason)
return (reasons[i].str);
}
snprintf(reasonbuf, sizeof(reasonbuf), "%#lx", reason);
return (reasonbuf);
}
#endif /* KTR */
@ -1524,6 +1539,20 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
case VMCB_EXIT_MWAIT:
vmexit->exitcode = VM_EXITCODE_MWAIT;
break;
case VMCB_EXIT_SHUTDOWN:
case VMCB_EXIT_VMRUN:
case VMCB_EXIT_VMMCALL:
case VMCB_EXIT_VMLOAD:
case VMCB_EXIT_VMSAVE:
case VMCB_EXIT_STGI:
case VMCB_EXIT_CLGI:
case VMCB_EXIT_SKINIT:
case VMCB_EXIT_ICEBP:
case VMCB_EXIT_INVD:
case VMCB_EXIT_INVLPGA:
vm_inject_ud(svm_sc->vm, vcpu);
handled = 1;
break;
default:
vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_UNKNOWN, 1);
break;

View File

@ -71,8 +71,8 @@
#define VMCB_INTCPT_INVD BIT(22)
#define VMCB_INTCPT_PAUSE BIT(23)
#define VMCB_INTCPT_HLT BIT(24)
#define VMCB_INTCPT_INVPG BIT(25)
#define VMCB_INTCPT_INVPGA BIT(26)
#define VMCB_INTCPT_INVLPG BIT(25)
#define VMCB_INTCPT_INVLPGA BIT(26)
#define VMCB_INTCPT_IO BIT(27)
#define VMCB_INTCPT_MSR BIT(28)
#define VMCB_INTCPT_TASK_SWITCH BIT(29)
@ -134,12 +134,21 @@
#define VMCB_EXIT_POPF 0x71
#define VMCB_EXIT_CPUID 0x72
#define VMCB_EXIT_IRET 0x74
#define VMCB_EXIT_INVD 0x76
#define VMCB_EXIT_PAUSE 0x77
#define VMCB_EXIT_HLT 0x78
#define VMCB_EXIT_INVLPGA 0x7A
#define VMCB_EXIT_IO 0x7B
#define VMCB_EXIT_MSR 0x7C
#define VMCB_EXIT_SHUTDOWN 0x7F
#define VMCB_EXIT_VMRUN 0x80
#define VMCB_EXIT_VMMCALL 0x81
#define VMCB_EXIT_VMLOAD 0x82
#define VMCB_EXIT_VMSAVE 0x83
#define VMCB_EXIT_STGI 0x84
#define VMCB_EXIT_CLGI 0x85
#define VMCB_EXIT_SKINIT 0x86
#define VMCB_EXIT_ICEBP 0x88
#define VMCB_EXIT_MONITOR 0x8A
#define VMCB_EXIT_MWAIT 0x8B
#define VMCB_EXIT_NPF 0x400