xorl %eax, %eax

CVE-2010-0435: Red Hat Enterprise Virtualization and KVM Pointer Dereference

with one comment

This bug was reported by Gleb Napatov. The vulnerability affects Red Hat Enterprise Virtualization and consequently KVM. The buggy code of KVM is Intel VT-x specific and can be found at arch/x86/kvm/x86.c.

int emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long *dest)
{
        struct kvm_vcpu *vcpu = ctxt->vcpu;

        switch (dr) {
        case 0 ... 3:
                *dest = kvm_x86_ops->get_dr(vcpu, dr);
                return X86EMUL_CONTINUE;
        default:
                pr_unimpl(vcpu, "%s: unexpected dr %u\n", __func__, dr);
                return X86EMUL_UNHANDLEABLE;
        }
}

As you can see, this simple routine will invoke a callback function named get_dr() that will use the passed virtual CPU and the ‘dr’ value to update ‘dest’ pointer with the debug register’s value. In case of ‘dr’ values greater than 3 it will log the event and return with an emulation error code that it was unable to handle it.
However, the get_dr() as well as its equivalent set_dr() function pointers were uninitialized. Here is the equivalent setting function of KVM:

int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long value)
{
        unsigned long mask = (ctxt->mode == X86EMUL_MODE_PROT64) ? ~0ULL : ~0U;
        int exception;

        kvm_x86_ops->set_dr(ctxt->vcpu, dr, value & mask, &exception);
        if (exception) {
                /* FIXME: better handling */
                return X86EMUL_UNHANDLEABLE;
        }
        return X86EMUL_CONTINUE;
}

The trigger of this bug can be done from unprivileged users inside a virtual machine leading to a NULL pointer deference in the hypervisor’s kernel. From the original bug report we can read:

If emulator is tricked into emulating mov to/from DR instruction
it causes NULL pointer dereference on VMX

I won’t discuss any further its exploitation although most people should already get the idea. Finally, the patch was to add checks before calling the two function pointers as shown below.

 	struct kvm_vcpu *vcpu = ctxt->vcpu;
 
+	if (!kvm_x86_ops->get_dr)
+		return X86EMUL_UNHANDLEABLE;
+
 	switch (dr) {
 	case 0 ... 3:

And for the emulator_set_dr() one…

 
+	if (!kvm_x86_ops->set_dr)
+		return X86EMUL_UNHANDLEABLE;
+
 	kvm_x86_ops->set_dr(ctxt->vcpu, dr, value & mask, &exception);

Written by xorl

January 3, 2011 at 22:54

Posted in bugs, linux

One Response

Subscribe to comments with RSS.

  1. All ’round well written article..

    JESSE DZIEDZIC

    October 20, 2011 at 19:31


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s