uacce: ensure safe queue release with state management

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>

authored by Chenghai Huang and committed by Greg Kroah-Hartman 26c08dab 02695347

+21 -7
+21 -7
drivers/misc/uacce/uacce.c
··· 40 40 return 0; 41 41 } 42 42 43 - static int uacce_put_queue(struct uacce_queue *q) 43 + static int uacce_stop_queue(struct uacce_queue *q) 44 44 { 45 45 struct uacce_device *uacce = q->uacce; 46 46 47 - if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue) 47 + if (q->state != UACCE_Q_STARTED) 48 + return 0; 49 + 50 + if (uacce->ops->stop_queue) 48 51 uacce->ops->stop_queue(q); 49 52 50 - if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) && 51 - uacce->ops->put_queue) 53 + q->state = UACCE_Q_INIT; 54 + 55 + return 0; 56 + } 57 + 58 + static void uacce_put_queue(struct uacce_queue *q) 59 + { 60 + struct uacce_device *uacce = q->uacce; 61 + 62 + uacce_stop_queue(q); 63 + 64 + if (q->state != UACCE_Q_INIT) 65 + return; 66 + 67 + if (uacce->ops->put_queue) 52 68 uacce->ops->put_queue(q); 53 69 54 70 q->state = UACCE_Q_ZOMBIE; 55 - 56 - return 0; 57 71 } 58 72 59 73 static long uacce_fops_unl_ioctl(struct file *filep, ··· 94 80 ret = uacce_start_queue(q); 95 81 break; 96 82 case UACCE_CMD_PUT_Q: 97 - ret = uacce_put_queue(q); 83 + ret = uacce_stop_queue(q); 98 84 break; 99 85 default: 100 86 if (uacce->ops->ioctl)