Re: [PATCH v6 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING

Anthony Krowiak <[email protected]>
Newsgroups dev.linux.lists.sashiko-reviews,org.kernel.vger.kvm,org.kernel.vger.linux-s390
Message-ID <[email protected]>

On 7/27/26 2:14 PM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Critical] Use-After-Free in `vfio_ap_stop_copy_read` leading to kernel memory leak to userspace.
> - [Low] The `.release` callback logic for cleaning up the resuming and stop-copy file states is completely unreachable dead code.
> --
>
> --- Patch [8]: [PATCH v6 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING ---
> commit 798f82863e85b4d37fd3594dc855fe7caaaaf7f5
> Author: Anthony Krowiak <[email protected]>
>
> s390/vfio-ap: Transition device migration state from STOP to RESUMING
>
> Transitioning from VFIO_DEVICE_STATE_STOP to VFIO_DEVICE_STATE_RESUMING
> starts a process of restoring the device state of the vfio device on the
> target system. To prepare for restoring the device state, open a file
> stream to receive the guest's AP configuration saved when the device state
> on the source system transitioned from VFIO_DEVICE_STATE_STOP to
> VFIO_DEVICE_STATE_STOP_COPY.
>
>> diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
>> index e4bc67b1eb842..c7fecad0b6769 100644
>> --- a/drivers/s390/crypto/vfio_ap_migration.c
>> +++ b/drivers/s390/crypto/vfio_ap_migration.c
> [ ... ]
>
>> @@ -99,6 +108,8 @@ static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
>>   
>>   	if (filp == matrix_mdev->mig_data->stop_copy_mig_file.filp)
>>   		vfio_ap_release_stop_copy_file(matrix_mdev->mig_data);
>> +	else if (filp == matrix_mdev->mig_data->resuming_mig_file.filp)
>> +		vfio_ap_release_resuming_file(matrix_mdev->mig_data);
> [Severity: Low]
> Is this conditional branch actually reachable?
>
> During vfio_ap_open_file_stream(), the driver takes an internal reference to
> the file via get_file(). This means the VFS .release callback is only invoked
> after the driver explicitly drops its reference by calling fput().
>
> If fput() is called in vfio_ap_release_mig_files(), which first sets
> mig_data->resuming_mig_file.filp = NULL, won't filp inside mig_data always
> be NULL by the time this .release callback executes?
>
> Consequently, will this check always evaluate to false, leaving
> vfio_ap_release_resuming_file() as unused dead code?

This is correct; those branches are unreachable. The root cause is
that vfio_ap_release_mig_files() calls fput() on the stored filp and then
immediately sets it to NULL, before the deferred .release callback can
fire. Since fput() always defers __fput() via task_work or a workqueue
(it never calls .release synchronously while a mutex is held), the .release
callback always arrives after the pointer is already NULL — making both
else if branches dead code.

The design will be reworked.

>
> [ ... ]
>
>> @@ -400,6 +411,26 @@ static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
>>   	return filp;
>>   }
>>   
>> +static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
>> +				      size_t len, loff_t *pos)
>> +{
>> +	/* TODO */
>> +	return -EOPNOTSUPP;
>> +}
>> +
>> +static const struct file_operations vfio_ap_resume_fops = {
>> +	.owner = THIS_MODULE,
>> +	.write = vfio_ap_resuming_write,
>> +	.release = vfio_ap_release_mig_file,
>> +};
> [Severity: Critical]
> Could the migration file stream logic be vulnerable to a use-after-free when
> the device file descriptor is concurrently closed?
>
> Looking at the related vfio_ap_stop_copy_read() stream implementation, it
> drops mdevs_lock before calling copy_to_user():
>
> drivers/s390/crypto/vfio_ap_migration.c:vfio_ap_stop_copy_read() {
>      ...
>      mutex_unlock(&matrix_dev->mdevs_lock);
>
>      if (copy_to_user(buf, (char *)mig_file->ap_config + read_pos, len))
>          return -EFAULT;
>      ...
> }
>
> If the device file descriptor is concurrently closed, vfio_ap_mdev_release()
> could invoke vfio_ap_release_migration_data(), which acquires mdevs_lock and
> frees the mig_data structure.
>
> When vfio_ap_stop_copy_read() resumes, will it dereference the freed mig_data
> structure via mig_file->ap_config and leak kernel memory to userspace?

The use-after-free described is not present. The safety relies on two
invariants:

1. matrix_mdev (and therefore mig_data) cannot be freed while the migration
    FD is open. vfio_ap_open_file_stream() calls
    vfio_device_try_get_registration() which pins matrix_mdev until
    vfio_ap_release_mig_file() drops it. Device teardown
    (vfio_ap_mdev_close_device()) can free mig_data independently, but
    matrix_mdev itself remains valid.

2. mig_file->ap_config is allocated once in vfio_ap_get_config() and freed
    only in vfio_ap_release_stop_copy_file(), which requires mdevs_lock.
    Since vfio_ap_stop_copy_read() advances *pos and snapshots read_pos
    before dropping the lock, any concurrent path that acquires the lock and
    frees ap_config can only do so after the position has already been
    committed — and copy_to_user() only touches the buffer contents, not the
    pointer itself.

>
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.