mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
s390/vfio-ap: Fix NULL deref in status_show() during queue probe
When vfio_ap_mdev_probe_queue() creates the sysfs attribute group,
the queue's driver data has not yet been set. A concurrent read of
the 'status' attribute can therefore call dev_get_drvdata() and
get NULL, which is then passed directly to
vfio_ap_mdev_for_queue() where q->apqn is unconditionally
dereferenced, causing a NULL pointer dereference.
Fix this by acquiring the update locks before calling
sysfs_create_group(). The status_show() function acquires
guests_lock before reading the driver data, so any concurrent
read will block until after dev_set_drvdata() has been called
and the update locks are released.
As a bonus, the APQN no longer needs to be read from the queue
struct after allocation — it can be read directly from apdev
before allocation and stored in a local variable, which is then
assigned to q->apqn once the allocation succeeds.
Fixes: 260f3ea141 ("s390/vfio-ap: move probe and remove callbacks to vfio_ap_ops.c")
Cc: stable@vger.kernel.org
Signed-off-by: Anthony Krowiak <akrowiak@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
This commit is contained in:
parent
917f509bfb
commit
dd6f4ef6f8
|
|
@ -2321,6 +2321,23 @@ static ssize_t status_show(struct device *dev,
|
|||
mutex_lock(&matrix_dev->guests_lock);
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
q = dev_get_drvdata(&apdev->device);
|
||||
|
||||
/*
|
||||
* Make sure the drvdata has been set before proceeding. There is a
|
||||
* possibility that the drvdata was not set if the vfio_ap_queue object
|
||||
* could not be allocated when the queue device was probed. In that case,
|
||||
* the locks used in vfio_ap_mdev_probe_queue() are released prior to
|
||||
* removing the sysfs status attribute to avoid a lockdep
|
||||
* splat. That opens a very small window where the status attribute is
|
||||
* still available without the vfio_ap_queue object having been
|
||||
* stored in the device drvdata. In that case, indicate the queue is not
|
||||
* assigned.
|
||||
*/
|
||||
if (!q) {
|
||||
nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED);
|
||||
goto done;
|
||||
}
|
||||
|
||||
matrix_mdev = vfio_ap_mdev_for_queue(q);
|
||||
|
||||
/* If the queue is assigned to the matrix mediated device, then
|
||||
|
|
@ -2345,6 +2362,7 @@ static ssize_t status_show(struct device *dev,
|
|||
nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED);
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
mutex_unlock(&matrix_dev->guests_lock);
|
||||
|
||||
|
|
@ -2419,14 +2437,17 @@ void vfio_ap_mdev_unregister(void)
|
|||
|
||||
int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
|
||||
{
|
||||
int ret;
|
||||
int ret, apqn;
|
||||
struct vfio_ap_queue *q;
|
||||
DECLARE_BITMAP(apm_filtered, AP_DEVICES);
|
||||
struct ap_matrix_mdev *matrix_mdev;
|
||||
|
||||
apqn = to_ap_queue(&apdev->device)->qid;
|
||||
matrix_mdev = get_update_locks_by_apqn(apqn);
|
||||
|
||||
ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_release_locks;
|
||||
|
||||
q = kzalloc_obj(*q);
|
||||
if (!q) {
|
||||
|
|
@ -2434,11 +2455,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
|
|||
goto err_remove_group;
|
||||
}
|
||||
|
||||
q->apqn = to_ap_queue(&apdev->device)->qid;
|
||||
q->apqn = apqn;
|
||||
q->saved_isc = VFIO_AP_ISC_INVALID;
|
||||
memset(&q->reset_status, 0, sizeof(q->reset_status));
|
||||
INIT_WORK(&q->reset_work, apq_reset_check);
|
||||
matrix_mdev = get_update_locks_by_apqn(q->apqn);
|
||||
|
||||
if (matrix_mdev) {
|
||||
vfio_ap_mdev_link_queue(matrix_mdev, q);
|
||||
|
|
@ -2467,8 +2487,13 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
|
|||
return ret;
|
||||
|
||||
err_remove_group:
|
||||
release_update_locks_for_mdev(matrix_mdev);
|
||||
sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group);
|
||||
return ret;
|
||||
|
||||
err_release_locks:
|
||||
release_update_locks_for_mdev(matrix_mdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void vfio_ap_mdev_remove_queue(struct ap_device *apdev)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user