ASA-2019-00084 – Linux kernel: Use-after-free using emulated vmx preemption timer


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

CVE-2019-7221 in Ubuntu
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

We are not responsible for any data loss, device corruption or any other type of issue due to the use of any information mentioned in our security alerts.