All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Haruka Kuze" <harukak@zohomail.eu>
Cc: virtualization@lists.linux.dev,
	"Michael S. Tsirkin" <mst@redhat.com>,
	"Eugenio Perez" <eperezma@redhat.com>
Subject: Re: [PATCH v2 2/2] rpmb: add a virtio RPMB device driver
Date: Thu, 29 Oct 2026 13:00:29 +0000	[thread overview]
Message-ID: <sashiko-outbox-189341@kernel.org> (raw)
In-Reply-To: <20261029124132.2354314-3-harukak@zohomail.eu>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] rpmb: virtio_rpmb: incorrect reliable_wr_count calculation in virtio_rpmb_probe()
- [High] rpmb: virtio_rpmb: race on virtqueue in virtio_rpmb_done()
- [Medium] virtio_rpmb: avoid duplicate del_vqs() in virtio_rpmb_del_vq() after failed restore
--

commit df2c2241a3933ce84e1976592ab925a107995087
Author: Haruka Kuze <harukak@zohomail.eu>

rpmb: add a virtio RPMB device driver

This commit adds a driver for virtio RPMB devices (virtio ID 28) and
registers them with the RPMB subsystem as RPMB_TYPE_EMMC so in-kernel users
such as OP-TEE can access them.

Link: https://lore.kernel.org/all/20220405093759.1126835-4-alex.bennee@linaro.org/

> diff --git a/drivers/misc/virtio_rpmb.c b/drivers/misc/virtio_rpmb.c
> new file mode 100644
> index 0000000000000..4caca378fecc4
> --- /dev/null
> +++ b/drivers/misc/virtio_rpmb.c

[ ... ]

> +static void virtio_rpmb_done(struct virtqueue *vq)
> +{
> +	struct virtio_rpmb_req *req;
> +	unsigned int len;
> +
> +	while ((req = virtqueue_get_buf(vq, &len))) {
> +		req->len = len;
> +		complete(&req->done);
> +	}
> +}
> +
> +static int virtio_rpmb_xfer(struct virtio_rpmb *vr, void *out,
> +			    unsigned int out_len, void *in, unsigned int in_len)
> +{
> +	struct scatterlist out_sg, in_sg, *sgs[2] = { &out_sg, &in_sg };
> +	struct virtio_rpmb_req req;
> +	int ret;
> +
> +	sg_init_one(&out_sg, out, out_len);
> +	sg_init_one(&in_sg, in, in_len);
> +	init_completion(&req.done);
> +
> +	guard(mutex)(&vr->lock);
> +	if (!vr->vq)
> +		return -ENODEV;
> +
> +	ret = virtqueue_add_sgs(vr->vq, sgs, 1, 1, &req, GFP_KERNEL);
> +	if (ret)
> +		return ret;
> +
> +	virtqueue_kick(vr->vq);
> +	wait_for_completion(&req.done);
> +
> +	return req.len == in_len ? 0 : -EIO;
> +}

[Severity: High]
Can virtio_rpmb_done() race with virtio_rpmb_xfer() on the virtqueue state?

In virtio_rpmb_done(), once complete(&req->done) is called on the first loop
iteration, the task waiting in virtio_rpmb_xfer() can wake up, exit the
function, and release vr->lock while virtio_rpmb_done() is still looping to
call virtqueue_get_buf(vq, &len) again:

CPU0                                    CPU1
virtio_rpmb_done()                      virtio_rpmb_xfer()
  req = virtqueue_get_buf(vq, &len);
  complete(&req->done);
                                          wait_for_completion(&req.done);
                                          /* releases vr->lock */
                                        virtio_rpmb_xfer()
                                          /* acquires vr->lock */
  virtqueue_get_buf(vq, &len);            virtqueue_add_sgs(vr->vq, ...);
                                          virtqueue_kick(vr->vq);

Additionally, because virtqueue_add_sgs() exposes the available descriptor
before returning, a polling host or spurious interrupt can trigger
virtio_rpmb_done() concurrently with virtqueue_add_sgs() or virtqueue_kick().

Since virtio_ring requires callers to synchronize virtqueue_get_buf() against
virtqueue_add_sgs() and virtqueue_kick(), could this corrupt virtqueue state
(such as vq->free_head, vq->vq.num_free, and vq->num_added) or trigger the
START_USE(vq) / END_USE(vq) checks in drivers/virtio/virtio_ring.c when DEBUG
is enabled?

[ ... ]

> +static void virtio_rpmb_del_vq(struct virtio_rpmb *vr)
> +{
> +	struct virtio_device *vdev = vr->vdev;
> +
> +	scoped_guard(mutex, &vr->lock)
> +		vr->vq = NULL;
> +
> +	virtio_reset_device(vdev);
> +	vdev->config->del_vqs(vdev);
> +}

[Severity: Medium]
What happens if virtio_rpmb_restore() fails after virtio_rpmb_freeze() has
already torn down the virtqueue?

During suspend, virtio_rpmb_freeze() calls virtio_rpmb_del_vq(), which sets
vr->vq = NULL, resets the device, and calls vdev->config->del_vqs(vdev).

If virtio_rpmb_restore() -> virtio_rpmb_init_vq() -> virtio_find_single_vq()
later fails during resume, vr->vq remains NULL while the driver stays bound
to the device. When virtio_rpmb_remove() or a subsequent virtio_rpmb_freeze()
runs, virtio_rpmb_del_vq() will unconditionally call
vdev->config->del_vqs(vdev) a second time without a matching find_vqs(),
which on transports such as virtio_mmio calls free_irq() on an already-freed
interrupt.

Should virtio_rpmb_del_vq() return early if vr->vq is already NULL?

> +
> +static int virtio_rpmb_probe(struct virtio_device *vdev)
> +{
> +	struct rpmb_descr descr = {
> +		/* Frames and their semantics are that of eMMC */
> +		.type = RPMB_TYPE_EMMC,
> +		.route_frames = virtio_rpmb_route_frames,
> +		.dev_id = (u8 *)virtio_rpmb_dev_id,
> +		.dev_id_len = sizeof(virtio_rpmb_dev_id),
> +	};
> +	struct virtio_rpmb *vr;
> +	u8 capacity;
> +	int ret;
> +
> +	capacity = virtio_cread8(vdev, offsetof(struct virtio_rpmb_config,
> +						capacity));
> +	if (!capacity || capacity > 0x80)
> +		return dev_err_probe(&vdev->dev, -EINVAL,
> +				     "invalid capacity %u\n", capacity);
> +
> +	vr = devm_kzalloc(&vdev->dev, sizeof(*vr), GFP_KERNEL);
> +	if (!vr)
> +		return -ENOMEM;
> +
> +	vdev->priv = vr;
> +	vr->vdev = vdev;
> +	mutex_init(&vr->lock);
> +	vr->max_wr = virtio_cread8(vdev, offsetof(struct virtio_rpmb_config,
> +						  max_wr_cnt));
> +	vr->max_rd = virtio_cread8(vdev, offsetof(struct virtio_rpmb_config,
> +						  max_rd_cnt));
> +
> +	ret = virtio_rpmb_init_vq(vr);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * eMMC reports the reliable wr count in 512 byte sectors, but the device
> +	 * limit is in 256 byte blocks.
> +	 */
> +	descr.capacity = capacity;
> +	descr.reliable_wr_count = (vr->max_wr ?: U8_MAX) / 2;

[Severity: High]
Should descr.reliable_wr_count be set without dividing vr->max_wr by 2 here?

Each 256-byte RPMB data block is carried inside a single 512-byte
virtio_rpmb_frame (one 512-byte eMMC sector per 256-byte RPMB data payload).
In virtio_rpmb_route_frames(), vr->max_wr is checked directly against the
number of 512-byte frames:

drivers/misc/virtio_rpmb.c:virtio_rpmb_route_frames() {
	...
	case VIRTIO_RPMB_REQ_DATA_WRITE:
		if (req_len % VIRTIO_RPMB_FRAME_SIZE ||
		    resp_len != VIRTIO_RPMB_FRAME_SIZE)
			return -EINVAL;
		if (vr->max_wr && req_len / VIRTIO_RPMB_FRAME_SIZE > vr->max_wr)
			return -EINVAL;
	...
}

In the RPMB subsystem, descr.reliable_wr_count is also the number of RPMB
frames (256-byte data blocks) per write request, matching mmc_blk_rpmb_add()
in drivers/mmc/core/block.c:

drivers/mmc/core/block.c:mmc_blk_rpmb_add() {
	...
	struct rpmb_descr descr = {
		.type = RPMB_TYPE_EMMC,
		.route_frames = mmc_route_rpmb_frames,
		.reliable_wr_count = card->ext_csd.enhanced_rpmb_supported ?
				     2 : 32,
	...
}

Dividing by 2 in virtio_rpmb_probe() halves the advertised write count, and
when a device reports max_wr_cnt == 1, integer division truncates
descr.reliable_wr_count to 0.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029124132.2354314-1-harukak@zohomail.eu?part=2

      reply	other threads:[~2026-10-29 13:00 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 12:41 [PATCH v2 0/2] rpmb: add virtio RPMB driver Haruka Kuze
2026-10-29 12:41 ` Haruka Kuze via OP-TEE
2026-10-29 12:41 ` [PATCH v2 1/2] rpmb: stop the routing after unregister Haruka Kuze
2026-10-29 12:41   ` Haruka Kuze via OP-TEE
2026-10-29 12:47   ` sashiko-bot
2026-10-29 12:41 ` [PATCH v2 2/2] rpmb: add a virtio RPMB device driver Haruka Kuze
2026-10-29 12:41   ` Haruka Kuze via OP-TEE
2026-10-29 13:00   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-189341@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=eperezma@redhat.com \
    --cc=harukak@zohomail.eu \
    --cc=mst@redhat.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=virtualization@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.