* [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) @ 2024-04-17 16:54 Adam Dunlap via groups.io 2024-04-17 17:08 ` Ard Biesheuvel ` (2 more replies) 0 siblings, 3 replies; 15+ messages in thread From: Adam Dunlap via groups.io @ 2024-04-17 16:54 UTC (permalink / raw) To: devel, Borislav Petkov, Peter Gonda, Tom Lendacky; +Cc: Adam Dunlap Ensure that when a #VC exception happens, the instruction at the instruction pointer matches the instruction that is expected given the error code. This is to mitigate the ahoi WeSee attack [1] that could allow hypervisors to breach integrity and confidentiality of the firmware by maliciously injecting interrupts. This change is a translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC instruction emulation somewhat") [1] https://ahoi-attacks.github.io/wesee/ Cc: Borislav Petkov (AMD) <bp@alien8.de> Cc: Tom Lendacky <thomas.lendacky@amd.com> Signed-off-by: Adam Dunlap <acdunlap@google.com> --- OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 171 ++++++++++++++++++-- 1 file changed, 160 insertions(+), 11 deletions(-) diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c index 0fc30f7bc4..bd3e9f304a 100644 --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c @@ -532,8 +532,6 @@ MwaitExit ( IN CC_INSTRUCTION_DATA *InstructionData ) { - CcDecodeModRm (Regs, InstructionData); - Ghcb->SaveArea.Rax = Regs->Rax; CcExitVmgSetOffsetValid (Ghcb, GhcbRax); Ghcb->SaveArea.Rcx = Regs->Rcx; @@ -564,8 +562,6 @@ MonitorExit ( IN CC_INSTRUCTION_DATA *InstructionData ) { - CcDecodeModRm (Regs, InstructionData); - Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA CcExitVmgSetOffsetValid (Ghcb, GhcbRax); Ghcb->SaveArea.Rcx = Regs->Rcx; @@ -670,8 +666,6 @@ VmmCallExit ( { UINT64 Status; - CcDecodeModRm (Regs, InstructionData); - Ghcb->SaveArea.Rax = Regs->Rax; CcExitVmgSetOffsetValid (Ghcb, GhcbRax); Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); @@ -1603,8 +1597,6 @@ Dr7WriteExit ( Ext = &InstructionData->Ext; SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); - CcDecodeModRm (Regs, InstructionData); - // // MOV DRn always treats MOD == 3 no matter how encoded // @@ -1655,8 +1647,6 @@ Dr7ReadExit ( Ext = &InstructionData->Ext; SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); - CcDecodeModRm (Regs, InstructionData); - // // MOV DRn always treats MOD == 3 no matter how encoded // @@ -1671,6 +1661,160 @@ Dr7ReadExit ( return 0; } +/** + Check that the opcode matches the exit code for a #VC. + + Each exit code should only be raised while executing certain instructions. + Verify that rIP points to a correct instruction based on the exit code to + protect against maliciously injected interrupts via the hypervisor. If it does + not, report an unsupported event to the hypervisor. + + Decodes the ModRm byte into InstructionData if necessary. + + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication + Block + @param[in, out] Regs x64 processor context + @param[in, out] InstructionData Instruction parsing context + @param[in] ExitCode Exit code given by #VC. + + @retval 0 No problems detected. + @return New exception value to propagate + + +**/ +STATIC +UINT64 +VcCheckOpcodeBytes ( + IN OUT GHCB *Ghcb, + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN OUT CC_INSTRUCTION_DATA *InstructionData, + IN UINT64 ExitCode + ) +{ + UINT8 OpCode; + + // + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. + // + OpCode = *(InstructionData->OpCodes); + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { + OpCode = *(InstructionData->OpCodes + 1); + } + + switch (ExitCode) { + case SVM_EXIT_IOIO_PROT: + case SVM_EXIT_NPF: + /* handled separately */ + return 0; + + case SVM_EXIT_CPUID: + if (OpCode == 0xa2) { + return 0; + } + + break; + + case SVM_EXIT_INVD: + break; + + case SVM_EXIT_MONITOR: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc8)) { + return 0; + } + + break; + + case SVM_EXIT_MWAIT: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc9)) { + return 0; + } + + break; + + case SVM_EXIT_MSR: + /* RDMSR */ + if ((OpCode == 0x32) || + /* WRMSR */ + (OpCode == 0x30)) + { + return 0; + } + + break; + + case SVM_EXIT_RDPMC: + if (OpCode == 0x33) { + return 0; + } + + break; + + case SVM_EXIT_RDTSC: + if (OpCode == 0x31) { + return 0; + } + + break; + + case SVM_EXIT_RDTSCP: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { + return 0; + } + + break; + + case SVM_EXIT_DR7_READ: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x21) && + (InstructionData->Ext.ModRm.Reg == 7)) + { + return 0; + } + + break; + + case SVM_EXIT_VMMCALL: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { + return 0; + } + + break; + + case SVM_EXIT_DR7_WRITE: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x23) && + (InstructionData->Ext.ModRm.Reg == 7)) + { + return 0; + } + + break; + + case SVM_EXIT_WBINVD: + if (OpCode == 0x9) { + return 0; + } + + break; + + default: + break; + } + + return UnsupportedExit (Ghcb, Regs, InstructionData); +} + /** Handle a #VC exception. @@ -1773,7 +1917,12 @@ InternalVmgExitHandleVc ( CcInitInstructionData (&InstructionData, Ghcb, Regs); - Status = NaeExit (Ghcb, Regs, &InstructionData); + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); + + if (Status == 0) { + Status = NaeExit (Ghcb, Regs, &InstructionData); + } + if (Status == 0) { Regs->Rip += CcInstructionLength (&InstructionData); } else { -- 2.44.0.683.g7961c838ac-goog -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117926): https://edk2.groups.io/g/devel/message/117926 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-17 16:54 [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) Adam Dunlap via groups.io @ 2024-04-17 17:08 ` Ard Biesheuvel 2024-04-17 17:45 ` Adam Dunlap via groups.io 2024-04-18 12:15 ` Gerd Hoffmann 2024-04-19 15:12 ` Lendacky, Thomas via groups.io 2 siblings, 1 reply; 15+ messages in thread From: Ard Biesheuvel @ 2024-04-17 17:08 UTC (permalink / raw) To: devel, acdunlap, Jiewen Yao; +Cc: Borislav Petkov, Peter Gonda, Tom Lendacky (cc Jiewen) Please cc the OVMF maintainers when you send edk2 patches. (There is a Maintainers file in the root of the repo) On Wed, 17 Apr 2024 at 18:54, Adam Dunlap via groups.io <acdunlap=google.com@groups.io> wrote: > > Ensure that when a #VC exception happens, the instruction at the > instruction pointer matches the instruction that is expected given the > error code. This is to mitigate the ahoi WeSee attack [1] that could > allow hypervisors to breach integrity and confidentiality of the > firmware by maliciously injecting interrupts. This change is a > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > instruction emulation somewhat") > > [1] https://ahoi-attacks.github.io/wesee/ > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > Cc: Tom Lendacky <thomas.lendacky@amd.com> > Signed-off-by: Adam Dunlap <acdunlap@google.com> > --- > OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 171 ++++++++++++++++++-- > 1 file changed, 160 insertions(+), 11 deletions(-) > > diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > index 0fc30f7bc4..bd3e9f304a 100644 > --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > @@ -532,8 +532,6 @@ MwaitExit ( > IN CC_INSTRUCTION_DATA *InstructionData > ) > { > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Rcx = Regs->Rcx; > @@ -564,8 +562,6 @@ MonitorExit ( > IN CC_INSTRUCTION_DATA *InstructionData > ) > { > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Rcx = Regs->Rcx; > @@ -670,8 +666,6 @@ VmmCallExit ( > { > UINT64 Status; > > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); > @@ -1603,8 +1597,6 @@ Dr7WriteExit ( > Ext = &InstructionData->Ext; > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > - CcDecodeModRm (Regs, InstructionData); > - > // > // MOV DRn always treats MOD == 3 no matter how encoded > // > @@ -1655,8 +1647,6 @@ Dr7ReadExit ( > Ext = &InstructionData->Ext; > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > - CcDecodeModRm (Regs, InstructionData); > - > // > // MOV DRn always treats MOD == 3 no matter how encoded > // > @@ -1671,6 +1661,160 @@ Dr7ReadExit ( > return 0; > } > > +/** > + Check that the opcode matches the exit code for a #VC. > + > + Each exit code should only be raised while executing certain instructions. > + Verify that rIP points to a correct instruction based on the exit code to > + protect against maliciously injected interrupts via the hypervisor. If it does > + not, report an unsupported event to the hypervisor. > + > + Decodes the ModRm byte into InstructionData if necessary. > + > + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication > + Block > + @param[in, out] Regs x64 processor context > + @param[in, out] InstructionData Instruction parsing context > + @param[in] ExitCode Exit code given by #VC. > + > + @retval 0 No problems detected. > + @return New exception value to propagate > + > + > +**/ > +STATIC > +UINT64 > +VcCheckOpcodeBytes ( > + IN OUT GHCB *Ghcb, > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, > + IN OUT CC_INSTRUCTION_DATA *InstructionData, > + IN UINT64 ExitCode > + ) > +{ > + UINT8 OpCode; > + > + // > + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always > + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. > + // > + OpCode = *(InstructionData->OpCodes); > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > + OpCode = *(InstructionData->OpCodes + 1); > + } > + > + switch (ExitCode) { > + case SVM_EXIT_IOIO_PROT: > + case SVM_EXIT_NPF: > + /* handled separately */ > + return 0; > + > + case SVM_EXIT_CPUID: > + if (OpCode == 0xa2) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_INVD: > + break; > + > + case SVM_EXIT_MONITOR: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc8)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MWAIT: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MSR: > + /* RDMSR */ > + if ((OpCode == 0x32) || > + /* WRMSR */ > + (OpCode == 0x30)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDPMC: > + if (OpCode == 0x33) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDTSC: > + if (OpCode == 0x31) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDTSCP: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_DR7_READ: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x21) && > + (InstructionData->Ext.ModRm.Reg == 7)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_VMMCALL: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_DR7_WRITE: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x23) && > + (InstructionData->Ext.ModRm.Reg == 7)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_WBINVD: > + if (OpCode == 0x9) { > + return 0; > + } > + > + break; > + > + default: > + break; > + } > + > + return UnsupportedExit (Ghcb, Regs, InstructionData); > +} > + > /** > Handle a #VC exception. > > @@ -1773,7 +1917,12 @@ InternalVmgExitHandleVc ( > > CcInitInstructionData (&InstructionData, Ghcb, Regs); > > - Status = NaeExit (Ghcb, Regs, &InstructionData); > + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); > + > + if (Status == 0) { > + Status = NaeExit (Ghcb, Regs, &InstructionData); > + } > + This looks a bit dodgy. First of all, I have a personal dislike of this 'success handling' anti-pattern, but more importantly, it seems like we are relying here on VcCheckOpcodeBytes() never returning on failure, right? If so, that at least needs a comment. > if (Status == 0) { > Regs->Rip += CcInstructionLength (&InstructionData); > } else { > -- > 2.44.0.683.g7961c838ac-goog > > > > > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117927): https://edk2.groups.io/g/devel/message/117927 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-17 17:08 ` Ard Biesheuvel @ 2024-04-17 17:45 ` Adam Dunlap via groups.io 2024-04-18 8:03 ` Yao, Jiewen 0 siblings, 1 reply; 15+ messages in thread From: Adam Dunlap via groups.io @ 2024-04-17 17:45 UTC (permalink / raw) To: Ard Biesheuvel Cc: devel, Jiewen Yao, Borislav Petkov, Peter Gonda, Tom Lendacky, Erdem Aktas, Gerd Hoffmann, Michael Roth, Min Xu On Wed, Apr 17, 2024 at 10:08 AM Ard Biesheuvel <ardb@kernel.org> wrote: > > (cc Jiewen) > > Please cc the OVMF maintainers when you send edk2 patches. (There is a > Maintainers file in the root of the repo) Thanks, I added everyone returned from the GetMaintainer.py script. > On Wed, 17 Apr 2024 at 18:54, Adam Dunlap via groups.io > <acdunlap=google.com@groups.io> wrote: > > > > Ensure that when a #VC exception happens, the instruction at the > > instruction pointer matches the instruction that is expected given the > > error code. This is to mitigate the ahoi WeSee attack [1] that could > > allow hypervisors to breach integrity and confidentiality of the > > firmware by maliciously injecting interrupts. This change is a > > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > > instruction emulation somewhat") > > > > [1] https://ahoi-attacks.github.io/wesee/ > > > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > > Cc: Tom Lendacky <thomas.lendacky@amd.com> > > Signed-off-by: Adam Dunlap <acdunlap@google.com> > > --- > > OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 171 ++++++++++++++++++-- > > 1 file changed, 160 insertions(+), 11 deletions(-) > > > > diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > > index 0fc30f7bc4..bd3e9f304a 100644 > > --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > > +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > > @@ -532,8 +532,6 @@ MwaitExit ( > > IN CC_INSTRUCTION_DATA *InstructionData > > ) > > { > > - CcDecodeModRm (Regs, InstructionData); > > - > > Ghcb->SaveArea.Rax = Regs->Rax; > > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > > Ghcb->SaveArea.Rcx = Regs->Rcx; > > @@ -564,8 +562,6 @@ MonitorExit ( > > IN CC_INSTRUCTION_DATA *InstructionData > > ) > > { > > - CcDecodeModRm (Regs, InstructionData); > > - > > Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA > > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > > Ghcb->SaveArea.Rcx = Regs->Rcx; > > @@ -670,8 +666,6 @@ VmmCallExit ( > > { > > UINT64 Status; > > > > - CcDecodeModRm (Regs, InstructionData); > > - > > Ghcb->SaveArea.Rax = Regs->Rax; > > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > > Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); > > @@ -1603,8 +1597,6 @@ Dr7WriteExit ( > > Ext = &InstructionData->Ext; > > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > > > - CcDecodeModRm (Regs, InstructionData); > > - > > // > > // MOV DRn always treats MOD == 3 no matter how encoded > > // > > @@ -1655,8 +1647,6 @@ Dr7ReadExit ( > > Ext = &InstructionData->Ext; > > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > > > - CcDecodeModRm (Regs, InstructionData); > > - > > // > > // MOV DRn always treats MOD == 3 no matter how encoded > > // > > @@ -1671,6 +1661,160 @@ Dr7ReadExit ( > > return 0; > > } > > > > +/** > > + Check that the opcode matches the exit code for a #VC. > > + > > + Each exit code should only be raised while executing certain instructions. > > + Verify that rIP points to a correct instruction based on the exit code to > > + protect against maliciously injected interrupts via the hypervisor. If it does > > + not, report an unsupported event to the hypervisor. > > + > > + Decodes the ModRm byte into InstructionData if necessary. > > + > > + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication > > + Block > > + @param[in, out] Regs x64 processor context > > + @param[in, out] InstructionData Instruction parsing context > > + @param[in] ExitCode Exit code given by #VC. > > + > > + @retval 0 No problems detected. > > + @return New exception value to propagate > > + > > + > > +**/ > > +STATIC > > +UINT64 > > +VcCheckOpcodeBytes ( > > + IN OUT GHCB *Ghcb, > > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, > > + IN OUT CC_INSTRUCTION_DATA *InstructionData, > > + IN UINT64 ExitCode > > + ) > > +{ > > + UINT8 OpCode; > > + > > + // > > + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always > > + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. > > + // > > + OpCode = *(InstructionData->OpCodes); > > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > > + OpCode = *(InstructionData->OpCodes + 1); > > + } > > + > > + switch (ExitCode) { > > + case SVM_EXIT_IOIO_PROT: > > + case SVM_EXIT_NPF: > > + /* handled separately */ > > + return 0; > > + > > + case SVM_EXIT_CPUID: > > + if (OpCode == 0xa2) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_INVD: > > + break; > > + > > + case SVM_EXIT_MONITOR: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc8)) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_MWAIT: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc9)) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_MSR: > > + /* RDMSR */ > > + if ((OpCode == 0x32) || > > + /* WRMSR */ > > + (OpCode == 0x30)) > > + { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_RDPMC: > > + if (OpCode == 0x33) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_RDTSC: > > + if (OpCode == 0x31) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_RDTSCP: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_DR7_READ: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x21) && > > + (InstructionData->Ext.ModRm.Reg == 7)) > > + { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_VMMCALL: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_DR7_WRITE: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x23) && > > + (InstructionData->Ext.ModRm.Reg == 7)) > > + { > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_WBINVD: > > + if (OpCode == 0x9) { > > + return 0; > > + } > > + > > + break; > > + > > + default: > > + break; > > + } > > + > > + return UnsupportedExit (Ghcb, Regs, InstructionData); > > +} > > + > > /** > > Handle a #VC exception. > > > > @@ -1773,7 +1917,12 @@ InternalVmgExitHandleVc ( > > > > CcInitInstructionData (&InstructionData, Ghcb, Regs); > > > > - Status = NaeExit (Ghcb, Regs, &InstructionData); > > + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); > > + > > + if (Status == 0) { > > + Status = NaeExit (Ghcb, Regs, &InstructionData); > > + } > > + > > This looks a bit dodgy. First of all, I have a personal dislike of > this 'success handling' anti-pattern, but more importantly, it seems > like we are relying here on VcCheckOpcodeBytes() never returning on > failure, right? If so, that at least needs a comment. > If VcCheckOpcodeBytes() returns failure, that means it thinks that the #VC was invalid/injected maliciously and that the guest should abort. From reading the code in this file, it looks like calling UnsupportedExit() and returning its return value is the standard way of doing this. If UnsupportedExit() doesn't abort and instead returns normally for whatever reason, it will just ignore the exception which seems like acceptable behavior. Maybe add a comment like /* If the opcode does not match the exit code, do not process the exception */ If we could ensure that UnsupportedExit() always diverged (i.e. never returned) then the code could be a bit simpler since it wouldn't need to handle error cases. > > if (Status == 0) { > > Regs->Rip += CcInstructionLength (&InstructionData); > > } else { > > -- > > 2.44.0.683.g7961c838ac-goog > > > > > > > > > > > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117928): https://edk2.groups.io/g/devel/message/117928 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-17 17:45 ` Adam Dunlap via groups.io @ 2024-04-18 8:03 ` Yao, Jiewen 0 siblings, 0 replies; 15+ messages in thread From: Yao, Jiewen @ 2024-04-18 8:03 UTC (permalink / raw) To: Adam Dunlap, Ard Biesheuvel Cc: devel@edk2.groups.io, Borislav Petkov, Peter Gonda, Tom Lendacky, Aktas, Erdem, Gerd Hoffmann, Michael Roth, Xu, Min M Thanks Adam and Ard. Since this #VC specific hardening, I would rely on AMD people's expertise to fix it. I have no objection for the patch. Thank you Yao, Jiewen > -----Original Message----- > From: Adam Dunlap <acdunlap@google.com> > Sent: Thursday, April 18, 2024 1:45 AM > To: Ard Biesheuvel <ardb@kernel.org> > Cc: devel@edk2.groups.io; Yao, Jiewen <jiewen.yao@intel.com>; Borislav Petkov > <bp@alien8.de>; Peter Gonda <pgonda@google.com>; Tom Lendacky > <thomas.lendacky@amd.com>; Aktas, Erdem <erdemaktas@google.com>; Gerd > Hoffmann <kraxel@redhat.com>; Michael Roth <michael.roth@amd.com>; Xu, > Min M <min.m.xu@intel.com> > Subject: Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation > somewhat (CVE-2024-25742) > > On Wed, Apr 17, 2024 at 10:08 AM Ard Biesheuvel <ardb@kernel.org> wrote: > > > > (cc Jiewen) > > > > Please cc the OVMF maintainers when you send edk2 patches. (There is a > > Maintainers file in the root of the repo) > > Thanks, I added everyone returned from the GetMaintainer.py script. > > > On Wed, 17 Apr 2024 at 18:54, Adam Dunlap via groups.io > > <acdunlap=google.com@groups.io> wrote: > > > > > > Ensure that when a #VC exception happens, the instruction at the > > > instruction pointer matches the instruction that is expected given the > > > error code. This is to mitigate the ahoi WeSee attack [1] that could > > > allow hypervisors to breach integrity and confidentiality of the > > > firmware by maliciously injecting interrupts. This change is a > > > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > > > instruction emulation somewhat") > > > > > > [1] https://ahoi-attacks.github.io/wesee/ > > > > > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > > > Cc: Tom Lendacky <thomas.lendacky@amd.com> > > > Signed-off-by: Adam Dunlap <acdunlap@google.com> > > > --- > > > OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 171 ++++++++++++++++++-- > > > 1 file changed, 160 insertions(+), 11 deletions(-) > > > > > > diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > > > index 0fc30f7bc4..bd3e9f304a 100644 > > > --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > > > +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > > > @@ -532,8 +532,6 @@ MwaitExit ( > > > IN CC_INSTRUCTION_DATA *InstructionData > > > ) > > > { > > > - CcDecodeModRm (Regs, InstructionData); > > > - > > > Ghcb->SaveArea.Rax = Regs->Rax; > > > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > > > Ghcb->SaveArea.Rcx = Regs->Rcx; > > > @@ -564,8 +562,6 @@ MonitorExit ( > > > IN CC_INSTRUCTION_DATA *InstructionData > > > ) > > > { > > > - CcDecodeModRm (Regs, InstructionData); > > > - > > > Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA > > > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > > > Ghcb->SaveArea.Rcx = Regs->Rcx; > > > @@ -670,8 +666,6 @@ VmmCallExit ( > > > { > > > UINT64 Status; > > > > > > - CcDecodeModRm (Regs, InstructionData); > > > - > > > Ghcb->SaveArea.Rax = Regs->Rax; > > > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > > > Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); > > > @@ -1603,8 +1597,6 @@ Dr7WriteExit ( > > > Ext = &InstructionData->Ext; > > > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > > > > > - CcDecodeModRm (Regs, InstructionData); > > > - > > > // > > > // MOV DRn always treats MOD == 3 no matter how encoded > > > // > > > @@ -1655,8 +1647,6 @@ Dr7ReadExit ( > > > Ext = &InstructionData->Ext; > > > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > > > > > - CcDecodeModRm (Regs, InstructionData); > > > - > > > // > > > // MOV DRn always treats MOD == 3 no matter how encoded > > > // > > > @@ -1671,6 +1661,160 @@ Dr7ReadExit ( > > > return 0; > > > } > > > > > > +/** > > > + Check that the opcode matches the exit code for a #VC. > > > + > > > + Each exit code should only be raised while executing certain instructions. > > > + Verify that rIP points to a correct instruction based on the exit code to > > > + protect against maliciously injected interrupts via the hypervisor. If it does > > > + not, report an unsupported event to the hypervisor. > > > + > > > + Decodes the ModRm byte into InstructionData if necessary. > > > + > > > + @param[in, out] Ghcb Pointer to the Guest-Hypervisor > Communication > > > + Block > > > + @param[in, out] Regs x64 processor context > > > + @param[in, out] InstructionData Instruction parsing context > > > + @param[in] ExitCode Exit code given by #VC. > > > + > > > + @retval 0 No problems detected. > > > + @return New exception value to propagate > > > + > > > + > > > +**/ > > > +STATIC > > > +UINT64 > > > +VcCheckOpcodeBytes ( > > > + IN OUT GHCB *Ghcb, > > > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, > > > + IN OUT CC_INSTRUCTION_DATA *InstructionData, > > > + IN UINT64 ExitCode > > > + ) > > > +{ > > > + UINT8 OpCode; > > > + > > > + // > > > + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always > > > + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. > > > + // > > > + OpCode = *(InstructionData->OpCodes); > > > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > > > + OpCode = *(InstructionData->OpCodes + 1); > > > + } > > > + > > > + switch (ExitCode) { > > > + case SVM_EXIT_IOIO_PROT: > > > + case SVM_EXIT_NPF: > > > + /* handled separately */ > > > + return 0; > > > + > > > + case SVM_EXIT_CPUID: > > > + if (OpCode == 0xa2) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_INVD: > > > + break; > > > + > > > + case SVM_EXIT_MONITOR: > > > + CcDecodeModRm (Regs, InstructionData); > > > + > > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc8)) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_MWAIT: > > > + CcDecodeModRm (Regs, InstructionData); > > > + > > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc9)) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_MSR: > > > + /* RDMSR */ > > > + if ((OpCode == 0x32) || > > > + /* WRMSR */ > > > + (OpCode == 0x30)) > > > + { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_RDPMC: > > > + if (OpCode == 0x33) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_RDTSC: > > > + if (OpCode == 0x31) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_RDTSCP: > > > + CcDecodeModRm (Regs, InstructionData); > > > + > > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_DR7_READ: > > > + CcDecodeModRm (Regs, InstructionData); > > > + > > > + if ((OpCode == 0x21) && > > > + (InstructionData->Ext.ModRm.Reg == 7)) > > > + { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_VMMCALL: > > > + CcDecodeModRm (Regs, InstructionData); > > > + > > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_DR7_WRITE: > > > + CcDecodeModRm (Regs, InstructionData); > > > + > > > + if ((OpCode == 0x23) && > > > + (InstructionData->Ext.ModRm.Reg == 7)) > > > + { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + case SVM_EXIT_WBINVD: > > > + if (OpCode == 0x9) { > > > + return 0; > > > + } > > > + > > > + break; > > > + > > > + default: > > > + break; > > > + } > > > + > > > + return UnsupportedExit (Ghcb, Regs, InstructionData); > > > +} > > > + > > > /** > > > Handle a #VC exception. > > > > > > @@ -1773,7 +1917,12 @@ InternalVmgExitHandleVc ( > > > > > > CcInitInstructionData (&InstructionData, Ghcb, Regs); > > > > > > - Status = NaeExit (Ghcb, Regs, &InstructionData); > > > + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); > > > + > > > + if (Status == 0) { > > > + Status = NaeExit (Ghcb, Regs, &InstructionData); > > > + } > > > + > > > > This looks a bit dodgy. First of all, I have a personal dislike of > > this 'success handling' anti-pattern, but more importantly, it seems > > like we are relying here on VcCheckOpcodeBytes() never returning on > > failure, right? If so, that at least needs a comment. > > > > If VcCheckOpcodeBytes() returns failure, that means it thinks that the #VC was > invalid/injected maliciously and that the guest should abort. From reading the > code in this file, it looks like calling UnsupportedExit() and returning its > return value is the standard way of doing this. If UnsupportedExit() doesn't > abort and instead returns normally for whatever reason, it will just ignore the > exception which seems like acceptable behavior. Maybe add a comment like > > /* If the opcode does not match the exit code, do not process the exception */ > > If we could ensure that UnsupportedExit() always diverged (i.e. never returned) > then the code could be a bit simpler since it wouldn't need to handle error > cases. > > > > if (Status == 0) { > > > Regs->Rip += CcInstructionLength (&InstructionData); > > > } else { > > > -- > > > 2.44.0.683.g7961c838ac-goog > > > > > > > > > > > > > > > > > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117966): https://edk2.groups.io/g/devel/message/117966 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-17 16:54 [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) Adam Dunlap via groups.io 2024-04-17 17:08 ` Ard Biesheuvel @ 2024-04-18 12:15 ` Gerd Hoffmann 2024-04-18 15:39 ` Adam Dunlap via groups.io 2024-04-19 14:56 ` Lendacky, Thomas via groups.io 2024-04-19 15:12 ` Lendacky, Thomas via groups.io 2 siblings, 2 replies; 15+ messages in thread From: Gerd Hoffmann @ 2024-04-18 12:15 UTC (permalink / raw) To: devel, acdunlap; +Cc: Borislav Petkov, Peter Gonda, Tom Lendacky On Wed, Apr 17, 2024 at 09:54:00AM -0700, Adam Dunlap via groups.io wrote: > Ensure that when a #VC exception happens, the instruction at the > instruction pointer matches the instruction that is expected given the > error code. This is to mitigate the ahoi WeSee attack [1] that could > allow hypervisors to breach integrity and confidentiality of the > firmware by maliciously injecting interrupts. This change is a > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > instruction emulation somewhat") > +**/ > +STATIC > +UINT64 > +VcCheckOpcodeBytes ( > + IN OUT GHCB *Ghcb, > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, > + IN OUT CC_INSTRUCTION_DATA *InstructionData, > + IN UINT64 ExitCode > + ) > +{ > + UINT8 OpCode; The linux kernel patch uses "unsigned int opcode" and apparently checks more than just the first byte for multi-byte opcodes. Why do it differently here? On the bigger picture: I'm wondering why SNP allows external #VC injections in the first place? take care, Gerd -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117985): https://edk2.groups.io/g/devel/message/117985 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-18 12:15 ` Gerd Hoffmann @ 2024-04-18 15:39 ` Adam Dunlap via groups.io 2024-04-18 15:43 ` Peter Gonda via groups.io 2024-04-19 11:31 ` Gerd Hoffmann 2024-04-19 14:56 ` Lendacky, Thomas via groups.io 1 sibling, 2 replies; 15+ messages in thread From: Adam Dunlap via groups.io @ 2024-04-18 15:39 UTC (permalink / raw) To: Gerd Hoffmann; +Cc: devel, Borislav Petkov, Peter Gonda, Tom Lendacky On Thu, Apr 18, 2024 at 5:15 AM Gerd Hoffmann <kraxel@redhat.com> wrote: > > On Wed, Apr 17, 2024 at 09:54:00AM -0700, Adam Dunlap via groups.io wrote: > > + UINT8 OpCode; > > The linux kernel patch uses "unsigned int opcode" and apparently > checks more than just the first byte for multi-byte opcodes. Why > do it differently here? Good question. This patch does check for two-byte opcodes with this snippet: + OpCode = *(InstructionData->OpCodes); + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { + OpCode = *(InstructionData->OpCodes + 1); + } This works because the first byte of two-byte opcodes is always 0x0f in the cases that we're checking for. I was wary about blindly dereferencing two bytes since that could cause a page fault if it was actually a 1 byte opcode that was at the very end of an allocated region. This is also what is done in the MmioExit function in this file. The linux kernel instruction decoder is much more extensive than what is done here and I didn't want to duplicate the whole thing. > On the bigger picture: I'm wondering why SNP allows external #VC > injections in the first place? Yup, I think it'd be better if it didn't. -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117995): https://edk2.groups.io/g/devel/message/117995 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-18 15:39 ` Adam Dunlap via groups.io @ 2024-04-18 15:43 ` Peter Gonda via groups.io 2024-04-19 11:31 ` Gerd Hoffmann 1 sibling, 0 replies; 15+ messages in thread From: Peter Gonda via groups.io @ 2024-04-18 15:43 UTC (permalink / raw) To: Adam Dunlap; +Cc: Gerd Hoffmann, devel, Borislav Petkov, Tom Lendacky On Thu, Apr 18, 2024 at 9:39 AM Adam Dunlap <acdunlap@google.com> wrote: > > On Thu, Apr 18, 2024 at 5:15 AM Gerd Hoffmann <kraxel@redhat.com> wrote: > > > > On Wed, Apr 17, 2024 at 09:54:00AM -0700, Adam Dunlap via groups.io wrote: > > > + UINT8 OpCode; > > > > The linux kernel patch uses "unsigned int opcode" and apparently > > checks more than just the first byte for multi-byte opcodes. Why > > do it differently here? > > Good question. This patch does check for two-byte opcodes with this snippet: > > + OpCode = *(InstructionData->OpCodes); > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > + OpCode = *(InstructionData->OpCodes + 1); > + } > > This works because the first byte of two-byte opcodes is always 0x0f in the > cases that we're checking for. I was wary about blindly dereferencing two > bytes since that could cause a page fault if it was actually a 1 byte opcode > that was at the very end of an allocated region. This is also what is done in > the MmioExit function in this file. The linux kernel instruction decoder is much > more extensive than what is done here and I didn't want to duplicate the > whole thing. > > > On the bigger picture: I'm wondering why SNP allows external #VC > > injections in the first place? > > Yup, I think it'd be better if it didn't. I think this is a small mitigation until linux + edk2 guest's support restricted or alternate interrupt injection. I suggested Adam send this just to have parity between edk2 and linux. -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117996): https://edk2.groups.io/g/devel/message/117996 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-18 15:39 ` Adam Dunlap via groups.io 2024-04-18 15:43 ` Peter Gonda via groups.io @ 2024-04-19 11:31 ` Gerd Hoffmann 1 sibling, 0 replies; 15+ messages in thread From: Gerd Hoffmann @ 2024-04-19 11:31 UTC (permalink / raw) To: Adam Dunlap; +Cc: devel, Borislav Petkov, Peter Gonda, Tom Lendacky On Thu, Apr 18, 2024 at 08:39:20AM -0700, Adam Dunlap wrote: > On Thu, Apr 18, 2024 at 5:15 AM Gerd Hoffmann <kraxel@redhat.com> wrote: > > > > On Wed, Apr 17, 2024 at 09:54:00AM -0700, Adam Dunlap via groups.io wrote: > > > + UINT8 OpCode; > > > > The linux kernel patch uses "unsigned int opcode" and apparently > > checks more than just the first byte for multi-byte opcodes. Why > > do it differently here? > > Good question. This patch does check for two-byte opcodes with this snippet: > > + OpCode = *(InstructionData->OpCodes); > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > + OpCode = *(InstructionData->OpCodes + 1); > + } > > This works because the first byte of two-byte opcodes is always 0x0f in the > cases that we're checking for. Ok, missed that little detail. Thanks for explaining. Reviewed-by: Gerd Hoffmann <kraxel@redhat.com> take care, Gerd -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118018): https://edk2.groups.io/g/devel/message/118018 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-18 12:15 ` Gerd Hoffmann 2024-04-18 15:39 ` Adam Dunlap via groups.io @ 2024-04-19 14:56 ` Lendacky, Thomas via groups.io 1 sibling, 0 replies; 15+ messages in thread From: Lendacky, Thomas via groups.io @ 2024-04-19 14:56 UTC (permalink / raw) To: devel, kraxel, acdunlap; +Cc: Borislav Petkov, Peter Gonda On 4/18/24 07:15, Gerd Hoffmann via groups.io wrote: > On Wed, Apr 17, 2024 at 09:54:00AM -0700, Adam Dunlap via groups.io wrote: >> Ensure that when a #VC exception happens, the instruction at the >> instruction pointer matches the instruction that is expected given the >> error code. This is to mitigate the ahoi WeSee attack [1] that could >> allow hypervisors to breach integrity and confidentiality of the >> firmware by maliciously injecting interrupts. This change is a >> translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC >> instruction emulation somewhat") > >> +**/ >> +STATIC >> +UINT64 >> +VcCheckOpcodeBytes ( >> + IN OUT GHCB *Ghcb, >> + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, >> + IN OUT CC_INSTRUCTION_DATA *InstructionData, >> + IN UINT64 ExitCode >> + ) >> +{ >> + UINT8 OpCode; > > The linux kernel patch uses "unsigned int opcode" and apparently > checks more than just the first byte for multi-byte opcodes. Why > do it differently here? > > On the bigger picture: I'm wondering why SNP allows external #VC > injections in the first place? It does and it doesn't. It doesn't allow #VC when injected as an exception. But the case of #VC injected as an interrupt was missed (see the event injection type field). It will be fixed in hardware going forward, but for now... Thanks, Tom > > take care, > Gerd > > > > > > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118030): https://edk2.groups.io/g/devel/message/118030 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-17 16:54 [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) Adam Dunlap via groups.io 2024-04-17 17:08 ` Ard Biesheuvel 2024-04-18 12:15 ` Gerd Hoffmann @ 2024-04-19 15:12 ` Lendacky, Thomas via groups.io 2024-04-19 17:39 ` Adam Dunlap via groups.io 2 siblings, 1 reply; 15+ messages in thread From: Lendacky, Thomas via groups.io @ 2024-04-19 15:12 UTC (permalink / raw) To: Adam Dunlap, devel, Borislav Petkov, Peter Gonda On 4/17/24 11:54, Adam Dunlap wrote: > Ensure that when a #VC exception happens, the instruction at the > instruction pointer matches the instruction that is expected given the > error code. This is to mitigate the ahoi WeSee attack [1] that could > allow hypervisors to breach integrity and confidentiality of the > firmware by maliciously injecting interrupts. This change is a > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > instruction emulation somewhat") > > [1] https://ahoi-attacks.github.io/wesee/ > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > Cc: Tom Lendacky <thomas.lendacky@amd.com> > Signed-off-by: Adam Dunlap <acdunlap@google.com> > --- > OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 171 ++++++++++++++++++-- > 1 file changed, 160 insertions(+), 11 deletions(-) > > diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > index 0fc30f7bc4..bd3e9f304a 100644 > --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > @@ -532,8 +532,6 @@ MwaitExit ( > IN CC_INSTRUCTION_DATA *InstructionData > ) > { > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Rcx = Regs->Rcx; > @@ -564,8 +562,6 @@ MonitorExit ( > IN CC_INSTRUCTION_DATA *InstructionData > ) > { > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Rcx = Regs->Rcx; > @@ -670,8 +666,6 @@ VmmCallExit ( > { > UINT64 Status; > > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); > @@ -1603,8 +1597,6 @@ Dr7WriteExit ( > Ext = &InstructionData->Ext; > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > - CcDecodeModRm (Regs, InstructionData); > - > // > // MOV DRn always treats MOD == 3 no matter how encoded > // > @@ -1655,8 +1647,6 @@ Dr7ReadExit ( > Ext = &InstructionData->Ext; > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > - CcDecodeModRm (Regs, InstructionData); > - > // > // MOV DRn always treats MOD == 3 no matter how encoded > // > @@ -1671,6 +1661,160 @@ Dr7ReadExit ( > return 0; > } > > +/** > + Check that the opcode matches the exit code for a #VC. > + > + Each exit code should only be raised while executing certain instructions. > + Verify that rIP points to a correct instruction based on the exit code to > + protect against maliciously injected interrupts via the hypervisor. If it does > + not, report an unsupported event to the hypervisor. > + > + Decodes the ModRm byte into InstructionData if necessary. > + > + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication > + Block > + @param[in, out] Regs x64 processor context > + @param[in, out] InstructionData Instruction parsing context > + @param[in] ExitCode Exit code given by #VC. > + > + @retval 0 No problems detected. > + @return New exception value to propagate > + > + > +**/ > +STATIC > +UINT64 > +VcCheckOpcodeBytes ( > + IN OUT GHCB *Ghcb, > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, > + IN OUT CC_INSTRUCTION_DATA *InstructionData, > + IN UINT64 ExitCode > + ) > +{ > + UINT8 OpCode; > + > + // > + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always > + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. > + // > + OpCode = *(InstructionData->OpCodes); > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > + OpCode = *(InstructionData->OpCodes + 1); > + } > + > + switch (ExitCode) { > + case SVM_EXIT_IOIO_PROT: > + case SVM_EXIT_NPF: > + /* handled separately */ > + return 0; > + > + case SVM_EXIT_CPUID: > + if (OpCode == 0xa2) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_INVD: > + break; This changes the current behavior today, but I'm ok with that. > + > + case SVM_EXIT_MONITOR: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc8)) { This should also handle the MONITORX opcode (hmmm... I need to send a patch to the kernel). > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MWAIT: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc9)) { Same here for MWAITX. Thanks, Tom > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MSR: > + /* RDMSR */ > + if ((OpCode == 0x32) || > + /* WRMSR */ > + (OpCode == 0x30)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDPMC: > + if (OpCode == 0x33) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDTSC: > + if (OpCode == 0x31) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDTSCP: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_DR7_READ: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x21) && > + (InstructionData->Ext.ModRm.Reg == 7)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_VMMCALL: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_DR7_WRITE: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x23) && > + (InstructionData->Ext.ModRm.Reg == 7)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_WBINVD: > + if (OpCode == 0x9) { > + return 0; > + } > + > + break; > + > + default: > + break; > + } > + > + return UnsupportedExit (Ghcb, Regs, InstructionData); > +} > + > /** > Handle a #VC exception. > > @@ -1773,7 +1917,12 @@ InternalVmgExitHandleVc ( > > CcInitInstructionData (&InstructionData, Ghcb, Regs); > > - Status = NaeExit (Ghcb, Regs, &InstructionData); > + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); > + > + if (Status == 0) { > + Status = NaeExit (Ghcb, Regs, &InstructionData); > + } > + > if (Status == 0) { > Regs->Rip += CcInstructionLength (&InstructionData); > } else { -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118031): https://edk2.groups.io/g/devel/message/118031 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-19 15:12 ` Lendacky, Thomas via groups.io @ 2024-04-19 17:39 ` Adam Dunlap via groups.io 2024-04-19 18:21 ` [edk2-devel] [PATCH v2] " Adam Dunlap via groups.io 0 siblings, 1 reply; 15+ messages in thread From: Adam Dunlap via groups.io @ 2024-04-19 17:39 UTC (permalink / raw) To: Tom Lendacky; +Cc: devel, Borislav Petkov, Peter Gonda On Fri, Apr 19, 2024 at 8:13 AM Tom Lendacky <thomas.lendacky@amd.com> wrote: > > On 4/17/24 11:54, Adam Dunlap wrote: > > + > > + case SVM_EXIT_INVD: > > + break; > > This changes the current behavior today, but I'm ok with that. > Whoops, I should've checked that. Should we delete InvdExit() then, if it's dead code? > > + > > + case SVM_EXIT_MONITOR: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc8)) { > > This should also handle the MONITORX opcode (hmmm... I need to send a > patch to the kernel). > > > + return 0; > > + } > > + > > + break; > > + > > + case SVM_EXIT_MWAIT: > > + CcDecodeModRm (Regs, InstructionData); > > + > > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xc9)) { > > Same here for MWAITX. > > Thanks, > Tom Got it! I'll send out a new patch shortly if I can figure out how to use git send-email correctly. -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118037): https://edk2.groups.io/g/devel/message/118037 Mute This Topic: https://groups.io/mt/105581633/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* [edk2-devel] [PATCH v2] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-19 17:39 ` Adam Dunlap via groups.io @ 2024-04-19 18:21 ` Adam Dunlap via groups.io 2024-04-22 14:12 ` Lendacky, Thomas via groups.io 2024-04-23 9:27 ` Gerd Hoffmann 0 siblings, 2 replies; 15+ messages in thread From: Adam Dunlap via groups.io @ 2024-04-19 18:21 UTC (permalink / raw) To: devel, Borislav Petkov, Peter Gonda, Ard Biesheuvel, Erdem Aktas, Gerd Hoffmann, Jiewen Yao, Michael Roth, Min Xu, Tom Lendacky, Yuan Yu Cc: Adam Dunlap Ensure that when a #VC exception happens, the instruction at the instruction pointer matches the instruction that is expected given the error code. This is to mitigate the ahoi WeSee attack [1] that could allow hypervisors to breach integrity and confidentiality of the firmware by maliciously injecting interrupts. This change is a translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC instruction emulation somewhat") [1] https://ahoi-attacks.github.io/wesee/ Cc: Borislav Petkov (AMD) <bp@alien8.de> Cc: Tom Lendacky <thomas.lendacky@amd.com> Signed-off-by: Adam Dunlap <acdunlap@google.com> --- Patch changelog: V1 -> V2: Added the MWAITX/MONITORX opcodes. Added the opcode for INVD. Added a comment to explain skipping the exit handling if the opcode does not match. OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 184 ++++++++++++++++++-- 1 file changed, 173 insertions(+), 11 deletions(-) diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c index 0fc30f7bc4..31587586fe 100644 --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c @@ -532,8 +532,6 @@ MwaitExit ( IN CC_INSTRUCTION_DATA *InstructionData ) { - CcDecodeModRm (Regs, InstructionData); - Ghcb->SaveArea.Rax = Regs->Rax; CcExitVmgSetOffsetValid (Ghcb, GhcbRax); Ghcb->SaveArea.Rcx = Regs->Rcx; @@ -564,8 +562,6 @@ MonitorExit ( IN CC_INSTRUCTION_DATA *InstructionData ) { - CcDecodeModRm (Regs, InstructionData); - Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA CcExitVmgSetOffsetValid (Ghcb, GhcbRax); Ghcb->SaveArea.Rcx = Regs->Rcx; @@ -670,8 +666,6 @@ VmmCallExit ( { UINT64 Status; - CcDecodeModRm (Regs, InstructionData); - Ghcb->SaveArea.Rax = Regs->Rax; CcExitVmgSetOffsetValid (Ghcb, GhcbRax); Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); @@ -1603,8 +1597,6 @@ Dr7WriteExit ( Ext = &InstructionData->Ext; SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); - CcDecodeModRm (Regs, InstructionData); - // // MOV DRn always treats MOD == 3 no matter how encoded // @@ -1655,8 +1647,6 @@ Dr7ReadExit ( Ext = &InstructionData->Ext; SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); - CcDecodeModRm (Regs, InstructionData); - // // MOV DRn always treats MOD == 3 no matter how encoded // @@ -1671,6 +1661,170 @@ Dr7ReadExit ( return 0; } +/** + Check that the opcode matches the exit code for a #VC. + + Each exit code should only be raised while executing certain instructions. + Verify that rIP points to a correct instruction based on the exit code to + protect against maliciously injected interrupts via the hypervisor. If it does + not, report an unsupported event to the hypervisor. + + Decodes the ModRm byte into InstructionData if necessary. + + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication + Block + @param[in, out] Regs x64 processor context + @param[in, out] InstructionData Instruction parsing context + @param[in] ExitCode Exit code given by #VC. + + @retval 0 No problems detected. + @return New exception value to propagate + + +**/ +STATIC +UINT64 +VcCheckOpcodeBytes ( + IN OUT GHCB *Ghcb, + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN OUT CC_INSTRUCTION_DATA *InstructionData, + IN UINT64 ExitCode + ) +{ + UINT8 OpCode; + + // + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. + // + OpCode = *(InstructionData->OpCodes); + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { + OpCode = *(InstructionData->OpCodes + 1); + } + + switch (ExitCode) { + case SVM_EXIT_IOIO_PROT: + case SVM_EXIT_NPF: + /* handled separately */ + return 0; + + case SVM_EXIT_CPUID: + if (OpCode == 0xa2) { + return 0; + } + + break; + + case SVM_EXIT_INVD: + if (OpCode == 0x08) { + return 0; + } + + break; + + case SVM_EXIT_MONITOR: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && + ( (InstructionData->ModRm.Uint8 == 0xc8) /* MONITOR */ + || (InstructionData->ModRm.Uint8 == 0xfa))) /* MONITORX */ + { + return 0; + } + + break; + + case SVM_EXIT_MWAIT: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && + ( (InstructionData->ModRm.Uint8 == 0xc9) /* MWAIT */ + || (InstructionData->ModRm.Uint8 == 0xfb))) /* MWAITX */ + { + return 0; + } + + break; + + case SVM_EXIT_MSR: + /* RDMSR */ + if ((OpCode == 0x32) || + /* WRMSR */ + (OpCode == 0x30)) + { + return 0; + } + + break; + + case SVM_EXIT_RDPMC: + if (OpCode == 0x33) { + return 0; + } + + break; + + case SVM_EXIT_RDTSC: + if (OpCode == 0x31) { + return 0; + } + + break; + + case SVM_EXIT_RDTSCP: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { + return 0; + } + + break; + + case SVM_EXIT_DR7_READ: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x21) && + (InstructionData->Ext.ModRm.Reg == 7)) + { + return 0; + } + + break; + + case SVM_EXIT_VMMCALL: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { + return 0; + } + + break; + + case SVM_EXIT_DR7_WRITE: + CcDecodeModRm (Regs, InstructionData); + + if ((OpCode == 0x23) && + (InstructionData->Ext.ModRm.Reg == 7)) + { + return 0; + } + + break; + + case SVM_EXIT_WBINVD: + if (OpCode == 0x9) { + return 0; + } + + break; + + default: + break; + } + + return UnsupportedExit (Ghcb, Regs, InstructionData); +} + /** Handle a #VC exception. @@ -1773,7 +1927,15 @@ InternalVmgExitHandleVc ( CcInitInstructionData (&InstructionData, Ghcb, Regs); - Status = NaeExit (Ghcb, Regs, &InstructionData); + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); + + // + // If the opcode does not match the exit code, do not process the exception + // + if (Status == 0) { + Status = NaeExit (Ghcb, Regs, &InstructionData); + } + if (Status == 0) { Regs->Rip += CcInstructionLength (&InstructionData); } else { -- 2.44.0.769.g3c40516874-goog -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118038): https://edk2.groups.io/g/devel/message/118038 Mute This Topic: https://groups.io/mt/105623545/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-19 18:21 ` [edk2-devel] [PATCH v2] " Adam Dunlap via groups.io @ 2024-04-22 14:12 ` Lendacky, Thomas via groups.io 2024-04-23 9:27 ` Gerd Hoffmann 1 sibling, 0 replies; 15+ messages in thread From: Lendacky, Thomas via groups.io @ 2024-04-22 14:12 UTC (permalink / raw) To: devel, acdunlap, Borislav Petkov, Peter Gonda, Ard Biesheuvel, Erdem Aktas, Gerd Hoffmann, Jiewen Yao, Michael Roth, Min Xu, Yuan Yu On 4/19/24 13:21, Adam Dunlap via groups.io wrote: > Ensure that when a #VC exception happens, the instruction at the > instruction pointer matches the instruction that is expected given the > error code. This is to mitigate the ahoi WeSee attack [1] that could > allow hypervisors to breach integrity and confidentiality of the > firmware by maliciously injecting interrupts. This change is a > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > instruction emulation somewhat") > > [1] https://ahoi-attacks.github.io/wesee/ > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > Cc: Tom Lendacky <thomas.lendacky@amd.com> > Signed-off-by: Adam Dunlap <acdunlap@google.com> Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> > --- > > Patch changelog: > V1 -> V2: Added the MWAITX/MONITORX opcodes. Added the opcode for INVD. > Added a comment to explain skipping the exit handling if the opcode does > not match. > > OvmfPkg/Library/CcExitLib/CcExitVcHandler.c | 184 ++++++++++++++++++-- > 1 file changed, 173 insertions(+), 11 deletions(-) > > diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > index 0fc30f7bc4..31587586fe 100644 > --- a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > +++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c > @@ -532,8 +532,6 @@ MwaitExit ( > IN CC_INSTRUCTION_DATA *InstructionData > ) > { > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Rcx = Regs->Rcx; > @@ -564,8 +562,6 @@ MonitorExit ( > IN CC_INSTRUCTION_DATA *InstructionData > ) > { > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Rcx = Regs->Rcx; > @@ -670,8 +666,6 @@ VmmCallExit ( > { > UINT64 Status; > > - CcDecodeModRm (Regs, InstructionData); > - > Ghcb->SaveArea.Rax = Regs->Rax; > CcExitVmgSetOffsetValid (Ghcb, GhcbRax); > Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3); > @@ -1603,8 +1597,6 @@ Dr7WriteExit ( > Ext = &InstructionData->Ext; > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > - CcDecodeModRm (Regs, InstructionData); > - > // > // MOV DRn always treats MOD == 3 no matter how encoded > // > @@ -1655,8 +1647,6 @@ Dr7ReadExit ( > Ext = &InstructionData->Ext; > SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1); > > - CcDecodeModRm (Regs, InstructionData); > - > // > // MOV DRn always treats MOD == 3 no matter how encoded > // > @@ -1671,6 +1661,170 @@ Dr7ReadExit ( > return 0; > } > > +/** > + Check that the opcode matches the exit code for a #VC. > + > + Each exit code should only be raised while executing certain instructions. > + Verify that rIP points to a correct instruction based on the exit code to > + protect against maliciously injected interrupts via the hypervisor. If it does > + not, report an unsupported event to the hypervisor. > + > + Decodes the ModRm byte into InstructionData if necessary. > + > + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication > + Block > + @param[in, out] Regs x64 processor context > + @param[in, out] InstructionData Instruction parsing context > + @param[in] ExitCode Exit code given by #VC. > + > + @retval 0 No problems detected. > + @return New exception value to propagate > + > + > +**/ > +STATIC > +UINT64 > +VcCheckOpcodeBytes ( > + IN OUT GHCB *Ghcb, > + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, > + IN OUT CC_INSTRUCTION_DATA *InstructionData, > + IN UINT64 ExitCode > + ) > +{ > + UINT8 OpCode; > + > + // > + // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always > + // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that. > + // > + OpCode = *(InstructionData->OpCodes); > + if (OpCode == TWO_BYTE_OPCODE_ESCAPE) { > + OpCode = *(InstructionData->OpCodes + 1); > + } > + > + switch (ExitCode) { > + case SVM_EXIT_IOIO_PROT: > + case SVM_EXIT_NPF: > + /* handled separately */ > + return 0; > + > + case SVM_EXIT_CPUID: > + if (OpCode == 0xa2) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_INVD: > + if (OpCode == 0x08) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MONITOR: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && > + ( (InstructionData->ModRm.Uint8 == 0xc8) /* MONITOR */ > + || (InstructionData->ModRm.Uint8 == 0xfa))) /* MONITORX */ > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MWAIT: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && > + ( (InstructionData->ModRm.Uint8 == 0xc9) /* MWAIT */ > + || (InstructionData->ModRm.Uint8 == 0xfb))) /* MWAITX */ > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_MSR: > + /* RDMSR */ > + if ((OpCode == 0x32) || > + /* WRMSR */ > + (OpCode == 0x30)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDPMC: > + if (OpCode == 0x33) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDTSC: > + if (OpCode == 0x31) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_RDTSCP: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_DR7_READ: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x21) && > + (InstructionData->Ext.ModRm.Reg == 7)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_VMMCALL: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_DR7_WRITE: > + CcDecodeModRm (Regs, InstructionData); > + > + if ((OpCode == 0x23) && > + (InstructionData->Ext.ModRm.Reg == 7)) > + { > + return 0; > + } > + > + break; > + > + case SVM_EXIT_WBINVD: > + if (OpCode == 0x9) { > + return 0; > + } > + > + break; > + > + default: > + break; > + } > + > + return UnsupportedExit (Ghcb, Regs, InstructionData); > +} > + > /** > Handle a #VC exception. > > @@ -1773,7 +1927,15 @@ InternalVmgExitHandleVc ( > > CcInitInstructionData (&InstructionData, Ghcb, Regs); > > - Status = NaeExit (Ghcb, Regs, &InstructionData); > + Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode); > + > + // > + // If the opcode does not match the exit code, do not process the exception > + // > + if (Status == 0) { > + Status = NaeExit (Ghcb, Regs, &InstructionData); > + } > + > if (Status == 0) { > Regs->Rip += CcInstructionLength (&InstructionData); > } else { -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118096): https://edk2.groups.io/g/devel/message/118096 Mute This Topic: https://groups.io/mt/105623545/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-19 18:21 ` [edk2-devel] [PATCH v2] " Adam Dunlap via groups.io 2024-04-22 14:12 ` Lendacky, Thomas via groups.io @ 2024-04-23 9:27 ` Gerd Hoffmann 2024-04-24 16:27 ` Ard Biesheuvel 1 sibling, 1 reply; 15+ messages in thread From: Gerd Hoffmann @ 2024-04-23 9:27 UTC (permalink / raw) To: Adam Dunlap Cc: devel, Borislav Petkov, Peter Gonda, Ard Biesheuvel, Erdem Aktas, Jiewen Yao, Michael Roth, Min Xu, Tom Lendacky, Yuan Yu On Fri, Apr 19, 2024 at 11:21:46AM -0700, Adam Dunlap wrote: > Ensure that when a #VC exception happens, the instruction at the > instruction pointer matches the instruction that is expected given the > error code. This is to mitigate the ahoi WeSee attack [1] that could > allow hypervisors to breach integrity and confidentiality of the > firmware by maliciously injecting interrupts. This change is a > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > instruction emulation somewhat") > > [1] https://ahoi-attacks.github.io/wesee/ > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > Cc: Tom Lendacky <thomas.lendacky@amd.com> > Signed-off-by: Adam Dunlap <acdunlap@google.com> Reviewed-by: Gerd Hoffmann <kraxel@redhat.com> take care, Gerd -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118132): https://edk2.groups.io/g/devel/message/118132 Mute This Topic: https://groups.io/mt/105623545/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) 2024-04-23 9:27 ` Gerd Hoffmann @ 2024-04-24 16:27 ` Ard Biesheuvel 0 siblings, 0 replies; 15+ messages in thread From: Ard Biesheuvel @ 2024-04-24 16:27 UTC (permalink / raw) To: devel, kraxel Cc: Adam Dunlap, Borislav Petkov, Peter Gonda, Ard Biesheuvel, Erdem Aktas, Jiewen Yao, Michael Roth, Min Xu, Tom Lendacky, Yuan Yu On Tue, 23 Apr 2024 at 11:28, Gerd Hoffmann <kraxel@redhat.com> wrote: > > On Fri, Apr 19, 2024 at 11:21:46AM -0700, Adam Dunlap wrote: > > Ensure that when a #VC exception happens, the instruction at the > > instruction pointer matches the instruction that is expected given the > > error code. This is to mitigate the ahoi WeSee attack [1] that could > > allow hypervisors to breach integrity and confidentiality of the > > firmware by maliciously injecting interrupts. This change is a > > translated version of a linux patch e3ef461af35a ("x86/sev: Harden #VC > > instruction emulation somewhat") > > > > [1] https://ahoi-attacks.github.io/wesee/ > > > > Cc: Borislav Petkov (AMD) <bp@alien8.de> > > Cc: Tom Lendacky <thomas.lendacky@amd.com> > > Signed-off-by: Adam Dunlap <acdunlap@google.com> > > Reviewed-by: Gerd Hoffmann <kraxel@redhat.com> > Thanks all, I've merged this now. -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#118221): https://edk2.groups.io/g/devel/message/118221 Mute This Topic: https://groups.io/mt/105623545/7686176 Mute #vc:https://edk2.groups.io/g/devel/mutehashtag/vc Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2024-04-24 16:27 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-04-17 16:54 [edk2-devel] [PATCH] OvmfPkg: Harden #VC instruction emulation somewhat (CVE-2024-25742) Adam Dunlap via groups.io 2024-04-17 17:08 ` Ard Biesheuvel 2024-04-17 17:45 ` Adam Dunlap via groups.io 2024-04-18 8:03 ` Yao, Jiewen 2024-04-18 12:15 ` Gerd Hoffmann 2024-04-18 15:39 ` Adam Dunlap via groups.io 2024-04-18 15:43 ` Peter Gonda via groups.io 2024-04-19 11:31 ` Gerd Hoffmann 2024-04-19 14:56 ` Lendacky, Thomas via groups.io 2024-04-19 15:12 ` Lendacky, Thomas via groups.io 2024-04-19 17:39 ` Adam Dunlap via groups.io 2024-04-19 18:21 ` [edk2-devel] [PATCH v2] " Adam Dunlap via groups.io 2024-04-22 14:12 ` Lendacky, Thomas via groups.io 2024-04-23 9:27 ` Gerd Hoffmann 2024-04-24 16:27 ` Ard Biesheuvel
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox