CVE-2024-25742 (C, published 2024-05-17T22:15Z)
Description: value: In the Linux kernel before 6.9, an untrusted hypervisor can inject virtual interrupt 29 (#VC) at any point in time and can trigger its handler. This affects AMD SEV-SNP and AMD SEV-ES.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/torvalds/linux
Files changed:
arch/x86/boot/compressed/sev.c
arch/x86/kernel/sev-shared.c
arch/x86/kernel/sev.c
Commit message: x86/sev: Harden #VC instruction emulation somewhat

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


Vulnerable code:
#define error(v)	pr_err(v)
#define has_cpuflag(f)	boot_cpu_has(f)
	enum es_result result;

Fixed code:
	result = vc_check_opcode_bytes(&ctxt, exit_code);
	if (result != ES_OK)
		goto finish;

#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__)
#define sev_printk(fmt, ...)
#define sev_printk_rtl(fmt, ...)
	u16 opcode = *(unsigned short *)regs->ip;
	/* Is it really a CPUID insn? */
	if (opcode != 0xa20f)
		goto fail;


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;
}
	enum es_result result = vc_check_opcode_bytes(ctxt, exit_code);

	if (result != ES_OK)
		return result;