Re: [PATCH 1/3] KVM: x86: Fix out-of-bounds access in kvm_recalculate_phys_map()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 5/26/23 02:07, Sean Christopherson wrote:
> On Thu, May 25, 2023, Michal Luczaj wrote:
>> @@ -265,10 +265,14 @@ static int kvm_recalculate_phys_map(struct kvm_apic_map *new,
>>  		 * mapped, i.e. is aliased to multiple vCPUs.  The optimized
>>  		 * map requires a strict 1:1 mapping between IDs and vCPUs.
>>  		 */
>> -		if (apic_x2apic_mode(apic))
>> +		if (apic_x2apic_mode(apic)) {
>> +			if (x2apic_id > new->max_apic_id)
>> +				return -EINVAL;
> 
> Hmm, disabling the optimized map just because userspace created a new vCPU is
> unfortunate and unnecessary.  Rather than return -EINVAL and only perform the
> check when x2APIC is enabled, what if we instead do the check immediately and
> return -E2BIG?  Then the caller can retry with a bigger array size.  Preemption
> is enabled and retries are bounded by the number of possible vCPUs, so I don't
> see any obvious issues with retrying.

Right, that makes perfect sense.

Just a note, it changes the logic a bit:

- x2apic_format: an overflowing x2apic_id won't be silently ignored.

- !x2apic_format: -E2BIG even for !apic_x2apic_mode() leads to an realloc
instead of "new->phys_map[xapic_id] = apic" right away.

> @@ -228,6 +228,12 @@ static int kvm_recalculate_phys_map(struct kvm_apic_map *new,
>  	u32 xapic_id = kvm_xapic_id(apic);
>  	u32 physical_id;
>  
> +	if (WARN_ON_ONCE(xapic_id >= new->max_apic_id))
> +		return -EINVAL;

Shouldn't it be ">" instead of ">="?

That said, xapic_id > new->max_apic_id means something terrible has happened as
kvm_xapic_id() returns u8 and max_apic_id should never be less than 255. Does
this qualify for KVM_BUG_ON?

> +	if (x2apic_id >= new->max_apic_id)
> +		return -E2BIG;

Probably ">"?

> @@ -366,6 +371,7 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
>  	unsigned long i;
>  	u32 max_id = 255; /* enough space for any xAPIC ID */
>  	bool xapic_id_mismatch = false;
> +	int r;
>  
>  	/* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map.  */
>  	if (atomic_read_acquire(&kvm->arch.apic_map_dirty) == CLEAN)
> @@ -386,6 +392,7 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
>  		return;
>  	}
>  
> +retry:
>  	kvm_for_each_vcpu(i, vcpu, kvm)
>  		if (kvm_apic_present(vcpu))
>  			max_id = max(max_id, kvm_x2apic_id(vcpu->arch.apic));
> @@ -404,9 +411,13 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
>  		if (!kvm_apic_present(vcpu))
>  			continue;
>  
> -		if (kvm_recalculate_phys_map(new, vcpu, &xapic_id_mismatch)) {
> +		r = kvm_recalculate_phys_map(new, vcpu, &xapic_id_mismatch);
> +		if (r) {
>  			kvfree(new);
>  			new = NULL;
> +			if (r == -E2BIG)
> +				goto retry;
> +
>  			goto out;
>  		}

Maybe it's not important, but what about moving xapic_id_mismatch
(re)initialization after "retry:"?



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux