Commit 26c08dab authored by Chenghai Huang's avatar Chenghai Huang Committed by Greg Kroah-Hartman
Browse files

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: 015d239a ("uacce: add uacce driver")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarChenghai Huang <huangchenghai2@huawei.com>
Signed-off-by: default avatarYang Shen <shenyang39@huawei.com>
Acked-by: default avatarZhangfei Gao <zhangfei.gao@linaro.org>
Link: https://patch.msgid.link/20251202061256.4158641-5-huangchenghai2@huawei.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 02695347
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -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)