Re: [PATCH v6 10/15] s390/vfio-ap: File ops called to resume the vfio device migration

Anthony Krowiak <[email protected]> Tue, 4 Aug 2026 11:33:03 -0400
Newsgroups dev.linux.lists.sashiko-reviews,org.kernel.vger.kvm,org.kernel.vger.linux-s390
Message-ID <[email protected]>

On 7/27/26 2:12 PM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [Critical] Double fetch vulnerability in the resuming write path allows out-of-bounds kernel reads.
> - [Critical] Use-After-Free during concurrent device teardown while executing copy_from_user.
> - [High] Lack of rate limiting in validation error paths allows userspace to flood the kernel log and cause a Denial of Service.
> - [High] Unsynchronized concurrent writes can trigger premature configuration validation on uninitialized memory.
> --
>
> --- Patch [10]: [PATCH v6 10/15] s390/vfio-ap: File ops called to resume the vfio device migration ---
> commit 56776518d352cfe0d799d4f95bc7d9c8703337bf
> Author: Anthony Krowiak <[email protected]>
> Subject: s390/vfio-ap: File ops called to resume the vfio device migration
>
> This commit implements the write callback for the vfio-ap device migration
> file stream. It restores the source guest's AP configuration by validating
> and hot-plugging it into the destination guest.
>
>> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
>> index c7fecad0b6769..c12ba82ec5277 100644
>> --- a/drivers/s390/crypto/vfio_ap_migration.c
>> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> [ ... ]
>
>> @@ -881,14 +881,20 @@
>>   static void report_qinfo_incompatibilities(const char *mdev_name,
>>   					   unsigned long apqn,
>>   					   struct ap_tapq_hwinfo *src_hwinfo,
>>   					   struct ap_tapq_hwinfo *target_hwinfo)
>>   {
>> +	pr_err("vfio_ap_mdev %s: Migration failed: Source and target queue (%02lx.%04lx) not compatible\n",
>> +	       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
> [Severity: High]
> Is it possible for userspace to flood the kernel log here?
>
> If a configuration with a large number of incompatible queues is provided,
> verify_ap_configs_are_compatible() continues processing rather than aborting.
>
> Since report_qinfo_incompatibilities() and its helpers issue unconditional
> pr_err() calls for each mismatch, could this cause CPU stalls or a denial
> of service?

While it is highly unlikely for this to happen since it would require 
that the
system have an inordinate number of incompatible queue devices, the
pr_err calls will be replaced with pr_err_ratelimited calls.

>
>> +
>> +	report_facilities_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
>> +	report_mode_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
> [ ... ]
>
>> @@ -1071,61 +1071,80 @@
>>   static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
>>   				      size_t len, loff_t *pos)
>>   {
> [ ... ]
>
>> +	/*
>> +	 * Snapshot and advance *pos under the lock before dropping it for
>> +	 * copy_from_user().  This prevents concurrent write()s on the same
>> +	 * stream file from computing the same destination offset and clobbering
>> +	 * each other's data or racing to reassign mig_data->resuming_mig_file.
>> +	 */
>> +	write_pos = *pos;
>> +	*pos += len;
>> +
>> +	mutex_unlock(&matrix_dev->mdevs_lock);
>> +
>> +	if (copy_from_user((char *)ap_config + write_pos, buf, len)) {
>> +		if (new_allocation)
>> +			kvfree(ap_config);
>> +		return -EIO;
>> +	}
> [Severity: Critical]
> Can a concurrent device teardown cause a use-after-free here?
>
> The mdevs_lock is dropped to perform copy_from_user(). If a concurrent
> thread closes the VFIO device, vfio_ap_release_mig_files() will call
> kvfree(mig_data->resuming_mig_file.ap_config). Would this result in
> copy_from_user() writing into freed kernel memory?

The vfio_ap_open_file_stream calls vfio_device_try_get_registration()
(see patch 06/15) which pins the vfio_device reference until
vfio_ap_release_mig_file() runs — and release only runs when the file
descriptor is closed. The file descriptor cannot be closed while a write()
is in progress on it. So matrix_mdev and its mig_data remain valid for
the lifetime of the write call.

>
> [Severity: Critical]
> Could this sequence introduce a double-fetch vulnerability?
>
> The first fetch in allocate_ap_config() reads num_queues to determine the
> allocation size. The lock is then dropped, and this second copy_from_user()
> overwrites the entire buffer, including the num_queues field.
>
> If userspace modifies num_queues between these fetches, could it inflate
> ap_config->num_queues beyond the allocated size, causing out-of-bounds
> reads in queues_available() when it loops over source_config->num_queues?

A fix will be provided for this in the next patch series.

>
>> +
>> +	/* Check if we've completed writing the entire configuration */
>> +	if (write_pos + len == cfg_sz) {
>> +		/*
>> +		 * do_post_copy_validation() calls ap_tapq() which is a slow
>> +		 * hardware instruction.  Run it before acquiring the update
>> +		 * locks to avoid holding guests_lock, kvm->lock, and
>> +		 * mdevs_lock across the hardware calls.
>> +		 */
>> +		ret = do_post_copy_validation(mdev_name, ap_config);
> [Severity: High]
> Does this completion check safely handle concurrent writes?
>
> If userspace issues asynchronous write() syscalls for different chunks of
> the payload, they might execute copy_from_user() concurrently. If the
> thread writing the final chunk completes its copy before threads writing
> earlier chunks finish theirs, could do_post_copy_validation() be called
> on a buffer that still contains uninitialized data?

The completion check could theoretically fire early if concurrent writes
are issued, but in that case the validation in do_post_copy_validation
would catch any resulting inconsistency and return an error — the worst
outcome is a failed migration, not a security vulnerability or kernel
corruption.

However, a write_in_progress flag will be added to vfio_ap_migration_data
that will be set when the vfio_ap_resuming_write function is called, unless
the flag has already been set, in which case the function will return 
-EBUSY.write_in_progress

>
>> +		if (ret < 0) {
>> +			if (new_allocation)
>> +				kvfree(ap_config);
>> +			return ret;
>> +		}
> [ ... ]
>