Allele Security Alert
ASA-2019-00084
Identifier(s)
ASA-2019-00084, CVE-2019-7221
Title
Use-after-free using emulated vmx preemption timer
Vendor(s)
Linux foundation
Product(s)
Linux kernel
Affected version(s)
Linux kernel versions before 5.0
Linux kernel versions 4.20.x before 4.20.8
Linux kernel versions 4.19.x before 4.19.21
Linux kernel versions 4.14.x before 4.14.99
Linux kernel versions 4.9.x before 4.9.156
Linux kernel versions 4.4.x before 4.4.175
Linux kernel versions 3.18.x before 3.18.135
Linux kernel versions 3.16.x before 3.16.64
Fixed version(s)
Linux kernel version 5.0
Linux kernel version 4.20.8
Linux kernel version 4.19.21
Linux kernel version 4.14.99
Linux kernel version 4.9.156
Linux kernel version 4.4.175
Linux kernel version 3.18.135
Linux kernel version 3.16.64
Linux kernel with the following commit:
KVM: nVMX: unconditionally cancel preemption timer in free_nested (CVE-2019-7221)
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ecec76885bcfe3294685dc363fd1273df0d5d65f
Proof of concept
Unknown
Description
A use-after-free vulnerability was found in the way the Linux kernel’s KVM hypervisor emulates a preemption timer for L2 guests when nested (=1) virtualization is enabled. This high resolution timer(hrtimer) runs when a L2 guest is active. After VM exit, the sync_vmcs12() timer object is stopped. The use-after-free occurs if the timer object is freed before calling sync_vmcs12() routine. A guest user/process could use this flaw to crash the host kernel resulting in a denial of service or, potentially, gain privileged access to a system.
Technical details
KVM emulates VMX preemption timers for L2 guests using a high resolution timer called preemption_timer. The timer is stored in the nested_vmx structure which is a member of vcpu_vmx.
If a L1 hypervisor uses this feature for a L2 guest, the timer gets started right before entering the L2 guest in nested_vmx_run->enter_vmx_non_root_mode-> prepare_vmcs02-> vmx_start_preemption_timer:
static void vmx_start_preemption_timer(struct kvm_vcpu *vcpu) { u64 preemption_timeout = get_vmcs12(vcpu)->vmx_preemption_timer_value; struct vcpu_vmx *vmx = to_vmx(vcpu); … preemption_timeout <<= VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE; preemption_timeout *= 1000000; do_div(preemption_timeout, vcpu->arch.virtual_tsc_khz); hrtimer_start(&vmx->nested.preemption_timer, ns_to_ktime(preemption_timeout), HRTIMER_MODE_REL); }
To ensure that the timer only runs while L2 is active, KVM calls hrtimer_cancel in sync_vmc12, which is invoked after a L2 VM exit:
static void sync_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { ... if (nested_cpu_has_preemption_timer(vmcs12)) { if (vmcs12->vm_exit_controls & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER) vmcs12->vmx_preemption_timer_value = vmx_get_preemption_timer_value(vcpu); hrtimer_cancel(&to_vmx(vcpu)->nested.preemption_timer); }
There is no locking in place to ensure that the kvm_vcpu structure, which stores preemption_timer, is not freed while the timer is active. This means that an attacker who can cause a call to vmx_free_vcpu without invoking sync_vmcs12 can trigger a highly exploitable use-after-free (controlled function pointer + first arg pointing to attacker controlled data).
It’s believed there is a number of ways to do this but one possible approach is to trigger a VM entry failure in enter_vmx_non_root_mode after the call to vmx_start_preemption timer:
KVM reimplements most VM entry checks in software and calls them before enter_vmx_non_root_mode. However, entry failures due to an invalid MSR load address happen after prepare_vmcs02 invokes vmx_start_preemption_timer:
if (prepare_vmcs02(vcpu, vmcs12, from_vmentry ? exit_qual : &dummy_exit_qual)) goto fail; if (from_vmentry) { nested_get_vmcs12_pages(vcpu); r = EXIT_REASON_MSR_LOAD_FAIL; *exit_qual = nested_vmx_load_msr(vcpu, vmcs12->vm_entry_msr_load_addr, vmcs12->vm_entry_msr_load_count); if (*exit_qual) goto fail;
Credits
Felix Wilhelm (Google Project Zero)
Reference(s)
Issue 1760: KVM: use-after-free using emulated vmx preemption timer
https://bugs.chromium.org/p/project-zero/issues/detail?id=1760
KVM: nVMX: unconditionally cancel preemption timer in free_nested (CVE-2019-7221)
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ecec76885bcfe3294685dc363fd1273df0d5d65f
KVM: nVMX: unconditionally cancel preemption timer in free_nested (CVE-2019-7221)
https://github.com/torvalds/linux/commit/ecec76885bcfe3294685dc363fd1273df0d5d65f
Linux 5.0
https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.0
Linux 4.20.8
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.20.8
Linux 4.19.21
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.21
Linux 4.14.99
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.14.99
Linux 4.9.156
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.9.156
Linux 4.4.175
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.4.175
Linux 3.18.135
https://cdn.kernel.org/pub/linux/kernel/v3.x/ChangeLog-3.18.135
Linux 3.16.64
https://cdn.kernel.org/pub/linux/kernel/v3.x/ChangeLog-3.16.64
CVE-2019-7221 - Red Hat Customer Portal
https://access.redhat.com/security/cve/CVE-2019-7221
CVE-2019-7221 | SUSE
https://www.suse.com/security/cve/CVE-2019-7221
https://people.canonical.com/~ubuntu-security/cve/CVE-2019-7221.html
CVE-2019-7221
https://security-tracker.debian.org/tracker/CVE-2019-7221
CVE-2019-7221
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-7221
CVE-2019-7221
https://nvd.nist.gov/vuln/detail/CVE-2019-7221
If there is any error in this alert or you wish a comprehensive analysis, let us know.
Last modified: November 29, 2019