[PATCH v3 8/9] s390/vfio-ap: Fix NULL deref in status_show() during queue probe

From: Anthony Krowiak

Date: Wed Aug 12 2026 - 08:01:54 EST


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: 260f3ea141382 ("s390/vfio-ap: move probe and remove callbacks to vfio_ap_ops.c")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Anthony Krowiak <akrowiak@xxxxxxxxxxxxx>
---
drivers/s390/crypto/vfio_ap_ops.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index cb1e5db69d8a..3652ba792be7 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -2419,14 +2419,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 +2437,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);
@@ -2468,6 +2470,8 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)

err_remove_group:
sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group);
+err_release_locks:
+ release_update_locks_for_mdev(matrix_mdev);
return ret;
}

--
2.53.0