CVE-2010-0435: Red Hat Enterprise Virtualization and KVM Pointer Dereference
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);
All ’round well written article..
JESSE DZIEDZIC
October 20, 2011 at 19:31