From ec1f0e633cad38c1f867d04180315804d5adb4cd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 31 Jan 2012 05:37:11 +0000 Subject: [PATCH] [x86] KVM: fix missing checks in syscall emulation (CVE-2012-0045) svn path=/dists/trunk/linux-2.6/; revision=18657 --- debian/changelog | 1 + ...truct-x86_emulate_ops-with-get_cpuid.patch | 78 ++++++++++ ...-missing-checks-in-syscall-emulation.patch | 145 ++++++++++++++++++ debian/patches/series/base | 2 + 4 files changed, 226 insertions(+) create mode 100644 debian/patches/bugfix/x86/KVM-x86-extend-struct-x86_emulate_ops-with-get_cpuid.patch create mode 100644 debian/patches/bugfix/x86/KVM-x86-fix-missing-checks-in-syscall-emulation.patch diff --git a/debian/changelog b/debian/changelog index 1d99e51de..8b80efa85 100644 --- a/debian/changelog +++ b/debian/changelog @@ -32,6 +32,7 @@ linux-2.6 (3.2.2-1) UNRELEASED; urgency=low * l2tp: l2tp_ip - fix possible oops on packet receive * macvlan: fix a possible use after free * tcp: fix tcp_trim_head() to adjust segment count with skb MSS + * [x86] KVM: fix missing checks in syscall emulation (CVE-2012-0045) [ Thorsten Glaser ] * [m68k] Use gcc-4.6 like (almost) all other architectures diff --git a/debian/patches/bugfix/x86/KVM-x86-extend-struct-x86_emulate_ops-with-get_cpuid.patch b/debian/patches/bugfix/x86/KVM-x86-extend-struct-x86_emulate_ops-with-get_cpuid.patch new file mode 100644 index 000000000..21546f4e5 --- /dev/null +++ b/debian/patches/bugfix/x86/KVM-x86-extend-struct-x86_emulate_ops-with-get_cpuid.patch @@ -0,0 +1,78 @@ +From: =?utf8?q?Stephan=20B=C3=A4rwolf?= +Date: Thu, 12 Jan 2012 16:43:03 +0100 +Subject: [PATCH] KVM: x86: extend "struct x86_emulate_ops" with "get_cpuid" + +commit 0769c5de24621141c953fbe1f943582d37cb4244 upstream. + +In order to be able to proceed checks on CPU-specific properties +within the emulator, function "get_cpuid" is introduced. +With "get_cpuid" it is possible to virtually call the guests +"cpuid"-opcode without changing the VM's context. + +[mtosatti: cleanup/beautify code] + +Signed-off-by: Stephan Baerwolf +Signed-off-by: Marcelo Tosatti +--- + arch/x86/include/asm/kvm_emulate.h | 3 +++ + arch/x86/kvm/x86.c | 23 +++++++++++++++++++++++ + 2 files changed, 26 insertions(+), 0 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h +index ab4092e..c8b2868 100644 +--- a/arch/x86/include/asm/kvm_emulate.h ++++ b/arch/x86/include/asm/kvm_emulate.h +@@ -190,6 +190,9 @@ struct x86_emulate_ops { + int (*intercept)(struct x86_emulate_ctxt *ctxt, + struct x86_instruction_info *info, + enum x86_intercept_stage stage); ++ ++ bool (*get_cpuid)(struct x86_emulate_ctxt *ctxt, ++ u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); + }; + + typedef u32 __attribute__((vector_size(16))) sse128_t; +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index f0fa3fb..c95ca2d 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -4205,6 +4205,28 @@ static int emulator_intercept(struct x86_emulate_ctxt *ctxt, + return kvm_x86_ops->check_intercept(emul_to_vcpu(ctxt), info, stage); + } + ++static bool emulator_get_cpuid(struct x86_emulate_ctxt *ctxt, ++ u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) ++{ ++ struct kvm_cpuid_entry2 *cpuid = NULL; ++ ++ if (eax && ecx) ++ cpuid = kvm_find_cpuid_entry(emul_to_vcpu(ctxt), ++ *eax, *ecx); ++ ++ if (cpuid) { ++ *eax = cpuid->eax; ++ *ecx = cpuid->ecx; ++ if (ebx) ++ *ebx = cpuid->ebx; ++ if (edx) ++ *edx = cpuid->edx; ++ return true; ++ } ++ ++ return false; ++} ++ + static struct x86_emulate_ops emulate_ops = { + .read_std = kvm_read_guest_virt_system, + .write_std = kvm_write_guest_virt_system, +@@ -4236,6 +4258,7 @@ static struct x86_emulate_ops emulate_ops = { + .get_fpu = emulator_get_fpu, + .put_fpu = emulator_put_fpu, + .intercept = emulator_intercept, ++ .get_cpuid = emulator_get_cpuid, + }; + + static void cache_all_regs(struct kvm_vcpu *vcpu) +-- +1.7.6.5 + diff --git a/debian/patches/bugfix/x86/KVM-x86-fix-missing-checks-in-syscall-emulation.patch b/debian/patches/bugfix/x86/KVM-x86-fix-missing-checks-in-syscall-emulation.patch new file mode 100644 index 000000000..73f218ed8 --- /dev/null +++ b/debian/patches/bugfix/x86/KVM-x86-fix-missing-checks-in-syscall-emulation.patch @@ -0,0 +1,145 @@ +From: =?utf8?q?Stephan=20B=C3=A4rwolf?= +Date: Thu, 12 Jan 2012 16:43:04 +0100 +Subject: [PATCH] KVM: x86: fix missing checks in syscall emulation + +commit e28ba7bb020f07193bc000453c8775e9d2c0dda7 upstream. + +On hosts without this patch, 32bit guests will crash (and 64bit guests +may behave in a wrong way) for example by simply executing following +nasm-demo-application: + + [bits 32] + global _start + SECTION .text + _start: syscall + +(I tested it with winxp and linux - both always crashed) + + Disassembly of section .text: + + 00000000 <_start>: + 0: 0f 05 syscall + +The reason seems a missing "invalid opcode"-trap (int6) for the +syscall opcode "0f05", which is not available on Intel CPUs +within non-longmodes, as also on some AMD CPUs within legacy-mode. +(depending on CPU vendor, MSR_EFER and cpuid) + +Because previous mentioned OSs may not engage corresponding +syscall target-registers (STAR, LSTAR, CSTAR), they remain +NULL and (non trapping) syscalls are leading to multiple +faults and finally crashs. + +Depending on the architecture (AMD or Intel) pretended by +guests, various checks according to vendor's documentation +are implemented to overcome the current issue and behave +like the CPUs physical counterparts. + +[mtosatti: cleanup/beautify code] + +Signed-off-by: Stephan Baerwolf +Signed-off-by: Marcelo Tosatti +--- + arch/x86/include/asm/kvm_emulate.h | 13 +++++++++ + arch/x86/kvm/emulate.c | 51 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+), 0 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h +index c8b2868..7b9cfc4 100644 +--- a/arch/x86/include/asm/kvm_emulate.h ++++ b/arch/x86/include/asm/kvm_emulate.h +@@ -301,6 +301,19 @@ struct x86_emulate_ctxt { + #define X86EMUL_MODE_PROT (X86EMUL_MODE_PROT16|X86EMUL_MODE_PROT32| \ + X86EMUL_MODE_PROT64) + ++/* CPUID vendors */ ++#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx 0x68747541 ++#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx 0x444d4163 ++#define X86EMUL_CPUID_VENDOR_AuthenticAMD_edx 0x69746e65 ++ ++#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx 0x69444d41 ++#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx 0x21726574 ++#define X86EMUL_CPUID_VENDOR_AMDisbetterI_edx 0x74656273 ++ ++#define X86EMUL_CPUID_VENDOR_GenuineIntel_ebx 0x756e6547 ++#define X86EMUL_CPUID_VENDOR_GenuineIntel_ecx 0x6c65746e ++#define X86EMUL_CPUID_VENDOR_GenuineIntel_edx 0x49656e69 ++ + enum x86_intercept_stage { + X86_ICTP_NONE = 0, /* Allow zero-init to not match anything */ + X86_ICPT_PRE_EXCEPT, +diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c +index 05a562b..0982507 100644 +--- a/arch/x86/kvm/emulate.c ++++ b/arch/x86/kvm/emulate.c +@@ -1891,6 +1891,51 @@ setup_syscalls_segments(struct x86_emulate_ctxt *ctxt, + ss->p = 1; + } + ++static bool em_syscall_is_enabled(struct x86_emulate_ctxt *ctxt) ++{ ++ struct x86_emulate_ops *ops = ctxt->ops; ++ u32 eax, ebx, ecx, edx; ++ ++ /* ++ * syscall should always be enabled in longmode - so only become ++ * vendor specific (cpuid) if other modes are active... ++ */ ++ if (ctxt->mode == X86EMUL_MODE_PROT64) ++ return true; ++ ++ eax = 0x00000000; ++ ecx = 0x00000000; ++ if (ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx)) { ++ /* ++ * Intel ("GenuineIntel") ++ * remark: Intel CPUs only support "syscall" in 64bit ++ * longmode. Also an 64bit guest with a ++ * 32bit compat-app running will #UD !! While this ++ * behaviour can be fixed (by emulating) into AMD ++ * response - CPUs of AMD can't behave like Intel. ++ */ ++ if (ebx == X86EMUL_CPUID_VENDOR_GenuineIntel_ebx && ++ ecx == X86EMUL_CPUID_VENDOR_GenuineIntel_ecx && ++ edx == X86EMUL_CPUID_VENDOR_GenuineIntel_edx) ++ return false; ++ ++ /* AMD ("AuthenticAMD") */ ++ if (ebx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx && ++ ecx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx && ++ edx == X86EMUL_CPUID_VENDOR_AuthenticAMD_edx) ++ return true; ++ ++ /* AMD ("AMDisbetter!") */ ++ if (ebx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx && ++ ecx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx && ++ edx == X86EMUL_CPUID_VENDOR_AMDisbetterI_edx) ++ return true; ++ } ++ ++ /* default: (not Intel, not AMD), apply Intel's stricter rules... */ ++ return false; ++} ++ + static int em_syscall(struct x86_emulate_ctxt *ctxt) + { + struct x86_emulate_ops *ops = ctxt->ops; +@@ -1904,9 +1949,15 @@ static int em_syscall(struct x86_emulate_ctxt *ctxt) + ctxt->mode == X86EMUL_MODE_VM86) + return emulate_ud(ctxt); + ++ if (!(em_syscall_is_enabled(ctxt))) ++ return emulate_ud(ctxt); ++ + ops->get_msr(ctxt, MSR_EFER, &efer); + setup_syscalls_segments(ctxt, &cs, &ss); + ++ if (!(efer & EFER_SCE)) ++ return emulate_ud(ctxt); ++ + ops->get_msr(ctxt, MSR_STAR, &msr_data); + msr_data >>= 32; + cs_sel = (u16)(msr_data & 0xfffc); +-- +1.7.6.5 + diff --git a/debian/patches/series/base b/debian/patches/series/base index 986716613..a0bb5a7ef 100644 --- a/debian/patches/series/base +++ b/debian/patches/series/base @@ -82,3 +82,5 @@ + bugfix/all/l2tp-l2tp_ip-fix-possible-oops-on-packet-receive.patch + bugfix/all/macvlan-fix-a-possible-use-after-free.patch + bugfix/all/tcp-fix-tcp_trim_head-to-adjust-segment-count-with-skb-MSS.patch ++ bugfix/x86/KVM-x86-extend-struct-x86_emulate_ops-with-get_cpuid.patch ++ bugfix/x86/KVM-x86-fix-missing-checks-in-syscall-emulation.patch