target-i386: kvm: prevent buffer overflow if -cpu foo, [x]level is too big

Stack corruption may occur if too big 'level' or 'xlevel' values passed
on command line with KVM enabled, due to limited size of cpuid_data
in kvm_arch_init_vcpu().

reproduces with:
 qemu -enable-kvm -cpu qemu64,level=4294967295
or
 qemu -enable-kvm -cpu qemu64,xlevel=4294967295

Check if there is space in cpuid_data before passing it to cpu_x86_cpuid()
or abort() if there is not space.

Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Andreas Faerber <afaerber@suse.de>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
This commit is contained in:
Igor Mammedov 2013-01-28 12:49:26 +01:00 committed by Gleb Natapov
parent f9e90c798d
commit f8bb056564

View File

@ -411,11 +411,12 @@ static void cpu_update_state(void *opaque, int running, RunState state)
} }
} }
#define KVM_MAX_CPUID_ENTRIES 100
int kvm_arch_init_vcpu(CPUState *cs) int kvm_arch_init_vcpu(CPUState *cs)
{ {
struct { struct {
struct kvm_cpuid2 cpuid; struct kvm_cpuid2 cpuid;
struct kvm_cpuid_entry2 entries[100]; struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
} QEMU_PACKED cpuid_data; } QEMU_PACKED cpuid_data;
X86CPU *cpu = X86_CPU(cs); X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
@ -502,6 +503,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
for (i = 0; i <= limit; i++) { for (i = 0; i <= limit; i++) {
if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
fprintf(stderr, "unsupported level value: 0x%x\n", limit);
abort();
}
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
switch (i) { switch (i) {
@ -516,6 +521,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
times = c->eax & 0xff; times = c->eax & 0xff;
for (j = 1; j < times; ++j) { for (j = 1; j < times; ++j) {
if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
fprintf(stderr, "cpuid_data is full, no space for "
"cpuid(eax:2):eax & 0xf = 0x%x\n", times);
abort();
}
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
c->function = i; c->function = i;
c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC;
@ -544,6 +554,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
if (i == 0xd && c->eax == 0) { if (i == 0xd && c->eax == 0) {
continue; continue;
} }
if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
fprintf(stderr, "cpuid_data is full, no space for "
"cpuid(eax:0x%x,ecx:0x%x)\n", i, j);
abort();
}
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
} }
break; break;
@ -557,6 +572,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused);
for (i = 0x80000000; i <= limit; i++) { for (i = 0x80000000; i <= limit; i++) {
if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit);
abort();
}
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
c->function = i; c->function = i;
@ -569,6 +588,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused);
for (i = 0xC0000000; i <= limit; i++) { for (i = 0xC0000000; i <= limit; i++) {
if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit);
abort();
}
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
c->function = i; c->function = i;