Re: [PATCH] LoongArch: KVM: Fix TOCTOU race on pv_features

Bibo Mao <[email protected]>
Newsgroups dev.linux.lists.loongarch,org.kernel.vger.kvm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>

On 2026/8/10 下午4:17, Tao Cui wrote:
> 
> Hi Bibo,
> 在 2026/8/10 16:13, Tao Cui 写道:
>> From: Tao Cui <[email protected]>
>>
>> kvm_loongarch_cpucfg_set_attr() validates and writes the VM-wide
>> pv_features with a lockless check-then-set, so two vCPUs racing it can
>> both pass the "all-vCPUs-must-match" check and install divergent values.
>> Make the check-then-set atomic with a cmpxchg loop; the UPDATED bit
>> already packs the configured state into the same word.
>>
> 
> I just sent a small patch for the pv_features TOCTOU we discussed. It
> turns the check-then-set into a cmpxchg loop on the existing UPDATED
> bit, so there's no new field or lock.
> 
> While working on it I also looked at two other options and wanted to
> mention them here.
> 
> One was a spinlock around the check-then-set. It works, but it needs a
> new kvm_arch member just for this, which didn't seem worth it given the
> UPDATED bit already keeps the value and flag in a single word.
> 
> The other is to move pv_features per-vCPU (vcpu->arch). That removes the
> shared state altogether: no lock, no latch, no cross-vCPU check, and the
> VMM just keeps the vCPUs in sync. It's the cleaner design, but a larger
> change, since QEMU would need a matching change too. Today QEMU pushes
> pv_features behind a process-wide `static int once` (only the first
> vCPU), which relies on the per-VM storage. This is the per-CPU direction
> you mentioned earlier [1]; if you're still planning to do it I'm happy to
> hold off, otherwise I can put together the kernel + QEMU side.
> 
> All three were built and tested locally with a vCPU-attribute test (the
> spinlock also came up clean under KCSAN).
> 
> Thanks,
> Tao
> 
> [1] https://lore.kernel.org/all/[email protected]/
> 
>> Signed-off-by: Tao Cui <[email protected]>
>> ---
>>   arch/loongarch/kvm/vcpu.c | 18 ++++++++++++------
>>   1 file changed, 12 insertions(+), 6 deletions(-)
>>
>> diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
>> index 20c207d80e31..55030c37cf06 100644
>> --- a/arch/loongarch/kvm/vcpu.c
>> +++ b/arch/loongarch/kvm/vcpu.c
>> @@ -1164,12 +1164,18 @@ static int kvm_loongarch_cpucfg_set_attr(struct kvm_vcpu *vcpu,
>>   		if (val & ~valid)
>>   			return -EINVAL;
>>   
>> -		/* All vCPUs need set the same PV features */
>> -		if ((kvm->arch.pv_features & LOONGARCH_PV_FEAT_UPDATED)
>> -				&& ((kvm->arch.pv_features & valid) != val))
>> -			return -EINVAL;
>> -		kvm->arch.pv_features = val | LOONGARCH_PV_FEAT_UPDATED;
>> -		return 0;
>> +		/* Atomically install val; the cmpxchg serializes concurrent setters. */
>> +		for (;;) {
>> +			unsigned long old, new;
>> +
>> +			old = READ_ONCE(kvm->arch.pv_features);
>> +			if ((old & LOONGARCH_PV_FEAT_UPDATED) &&
>> +			    ((old & valid) != val))
>> +				return -EINVAL;
>> +			new = val | LOONGARCH_PV_FEAT_UPDATED;
>> +			if (cmpxchg(&kvm->arch.pv_features, old, new) == old)
>> +				return 0;
>> +		}
The for loop sentence is a little strange, can we use spinlock method 
rather than atomic cmpxchg method? It is not performance sensitive here.

Regards
Bibo Mao
>>   	default:
>>   		return -ENXIO;
>>   	}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.