]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
uacce: ensure safe queue release with state management
authorChenghai Huang <huangchenghai2@huawei.com>
Tue, 2 Dec 2025 06:12:56 +0000 (14:12 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 16 Jan 2026 15:43:17 +0000 (16:43 +0100)
Directly calling `put_queue` carries risks since it cannot
guarantee that resources of `uacce_queue` have been fully released
beforehand. So adding a `stop_queue` operation for the
UACCE_CMD_PUT_Q command and leaving the `put_queue` operation to
the final resource release ensures safety.

Queue states are defined as follows:
- UACCE_Q_ZOMBIE: Initial state
- UACCE_Q_INIT: After opening `uacce`
- UACCE_Q_STARTED: After `start` is issued via `ioctl`

When executing `poweroff -f` in virt while accelerator are still
working, `uacce_fops_release` and `uacce_remove` may execute
concurrently. This can cause `uacce_put_queue` within
`uacce_fops_release` to access a NULL `ops` pointer. Therefore, add
state checks to prevent accessing freed pointers.

Fixes: 015d239ac014 ("uacce: add uacce driver")
Cc: stable@vger.kernel.org
Signed-off-by: Chenghai Huang <huangchenghai2@huawei.com>
Signed-off-by: Yang Shen <shenyang39@huawei.com>
Acked-by: Zhangfei Gao <zhangfei.gao@linaro.org>
Link: https://patch.msgid.link/20251202061256.4158641-5-huangchenghai2@huawei.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/uacce/uacce.c

index c061c6fa1c5e77278edd7351a33a322f7cb24c52..6d71355528d3441c2b6b33825402eacccc50a0e6 100644 (file)
@@ -40,20 +40,34 @@ static int uacce_start_queue(struct uacce_queue *q)
        return 0;
 }
 
-static int uacce_put_queue(struct uacce_queue *q)
+static int uacce_stop_queue(struct uacce_queue *q)
 {
        struct uacce_device *uacce = q->uacce;
 
-       if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
+       if (q->state != UACCE_Q_STARTED)
+               return 0;
+
+       if (uacce->ops->stop_queue)
                uacce->ops->stop_queue(q);
 
-       if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
-            uacce->ops->put_queue)
+       q->state = UACCE_Q_INIT;
+
+       return 0;
+}
+
+static void uacce_put_queue(struct uacce_queue *q)
+{
+       struct uacce_device *uacce = q->uacce;
+
+       uacce_stop_queue(q);
+
+       if (q->state != UACCE_Q_INIT)
+               return;
+
+       if (uacce->ops->put_queue)
                uacce->ops->put_queue(q);
 
        q->state = UACCE_Q_ZOMBIE;
-
-       return 0;
 }
 
 static long uacce_fops_unl_ioctl(struct file *filep,
@@ -80,7 +94,7 @@ static long uacce_fops_unl_ioctl(struct file *filep,
                ret = uacce_start_queue(q);
                break;
        case UACCE_CMD_PUT_Q:
-               ret = uacce_put_queue(q);
+               ret = uacce_stop_queue(q);
                break;
        default:
                if (uacce->ops->ioctl)