All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
* [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup
@ 2026-08-25 12:51 Andrey Zhadchenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 1/5] drivers/vhost/blk: harden get_id command Andrey Zhadchenko
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 12:51 UTC (permalink / raw)


This series fixes several vhost-blk bugs and harden some userspace
interactions.

First patch hardens VIRTIO_BLK_T_GET_ID. Leave the serial empty and
copy the null terminator, just as QEMU.

The second patch reports the correct used-ring length: the number of
bytes written to the guest, including status.

The third patch fixes flush. REQ_OP_FLUSH is not a valid bio op; use
REQ_OP_WRITE | REQ_PREFLUSH as blkdev_issue_flush() does.

The fourth patch fixes sector alignment checks. SECTOR_MASK is 7, not
a bitmask, so the code was checking 8-byte alignment. Use
IS_ALIGNED(val, SECTOR_SIZE).

The last patch reworks queue/backend setup. vhost_blk_setup() silently
ignored a changed vq->num once requests were allocated and
double-fetched user input. Request allocation is now tied to backend
existence, and drop_backend/flush/fput are consolidated.

Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>

v2:
 - Fix patch fixing used-ring length: only include req->len in read
requests.

v3:
 - Changed alignment checks to IS_ALIGNED macro.

Andrey Zhadchenko (5):
  drivers/vhost/blk: harden get_id command
  drivers/vhost/blk: report correct used-ring lengths
  drivers/vhost/blk: fix flush support
  drivers/vhost/blk: fix sector alignment calculation
  drivers/vhost/blk: rework queue/backend setup

 drivers/vhost/blk.c | 167 +++++++++++++++++++++-----------------------
 1 file changed, 78 insertions(+), 89 deletions(-)

-- 
2.43.5


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH VZ10 v3 1/5] drivers/vhost/blk: harden get_id command
  2026-08-25 12:51 [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup Andrey Zhadchenko
@ 2026-08-25 12:51 ` Andrey Zhadchenko
  2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 2/5] drivers/vhost/blk: report correct used-ring lengths Andrey Zhadchenko
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 12:51 UTC (permalink / raw)


A defensive patch.
QEMU is fine with reporting empty serial and including null
terminator. So we will do the same.

https://virtuozzo.atlassian.net/browse/VSTOR-138640
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index dac03566bfca5..edd3e75873ff5 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -105,8 +105,6 @@ struct vhost_blk {
 	pid_t owner_pid;
 };
 
-static int gen;
-
 static int move_iovec(struct iovec *from, struct iovec *to,
 		      size_t len, int iov_count_from, int iov_count_to)
 {
@@ -491,7 +489,9 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 		ret = vhost_blk_req_submit(req);
 		break;
 	case VIRTIO_BLK_T_GET_ID:
-		len = strnlen(blk->serial, VIRTIO_BLK_ID_BYTES);
+		len = min_t(size_t,
+			    strnlen(blk->serial, VIRTIO_BLK_ID_BYTES) + 1,
+			    min_t(size_t, req->len, VIRTIO_BLK_ID_BYTES));
 		iov_iter_init(&iter, ITER_DEST, req->iov, req->iov_nr, req->len);
 		ret = copy_to_iter(blk->serial, len, &iter);
 		status = ret != len ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
@@ -688,7 +688,6 @@ static int vhost_blk_open(struct inode *inode, struct file *file)
 	}
 
 	memset(blk->serial, 0, sizeof(blk->serial));
-	snprintf(blk->serial, VIRTIO_BLK_ID_BYTES, "vhost-blk%d", gen++);
 
 	atomic_set(&blk->req_inflight[0], 0);
 	atomic_set(&blk->req_inflight[1], 0);
-- 
2.43.5


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH VZ10 v3 2/5] drivers/vhost/blk: report correct used-ring lengths
  2026-08-25 12:51 [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup Andrey Zhadchenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 1/5] drivers/vhost/blk: harden get_id command Andrey Zhadchenko
@ 2026-08-25 12:51 ` Andrey Zhadchenko
  2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 3/5] drivers/vhost/blk: fix flush support Andrey Zhadchenko
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 12:51 UTC (permalink / raw)


We are expected to return amount of bytes written to the guest,
which also includes status.

https://virtuozzo.atlassian.net/browse/VSTOR-138640
Fixes: 40a5928ec730 ("drivers/vhost: vhost-blk accelerator for virtio-blk guests")
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index edd3e75873ff5..2c41a073004bc 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -445,12 +445,14 @@ static int vhost_blk_req_submit(struct vhost_blk_req *req)
 
 static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 				struct virtio_blk_outhdr *hdr,
-				u16 head, u16 total_iov_nr)
+				u16 head, u16 out, u16 in)
 {
 	struct vhost_blk *blk = container_of(vq->dev, struct vhost_blk, dev);
 	struct vhost_blk_vq *blk_vq = container_of(vq, struct vhost_blk_vq, vq);
 	struct vhost_blk_req *req;
+	u16 total_iov_nr = out + in;
 	struct iov_iter iter;
+	size_t in_len;
 	int ret, len;
 	u8 status;
 
@@ -461,8 +463,9 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 	req->sector	= hdr->sector;
 	req->iov	= blk_vq->iov;
 	req->bio_err	= 0;
+	in_len		= iov_length(vq->iov + out, in);
 
-	if (iov_length(vq->iov, total_iov_nr) < sizeof(status))
+	if (in_len < sizeof(status) || in_len > INT_MAX)
 		return -EINVAL;
 
 	req->len	= iov_length(vq->iov, total_iov_nr) - sizeof(status);
@@ -498,14 +501,14 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 		ret = vhost_blk_set_status(req, status);
 		if (ret)
 			break;
-		vhost_add_used_and_signal(&blk->dev, vq, head, len);
+		vhost_add_used_and_signal(&blk->dev, vq, head, in_len);
 		break;
 	default:
 		status = VIRTIO_BLK_S_UNSUPP;
 		ret = vhost_blk_set_status(req, status);
 		if (ret)
 			break;
-		vhost_add_used_and_signal(&blk->dev, vq, head, 0);
+		vhost_add_used_and_signal(&blk->dev, vq, head, sizeof(status));
 	}
 
 	return ret;
@@ -561,7 +564,7 @@ static void vhost_blk_handle_guest_kick(struct vhost_work *work)
 			break;
 		}
 
-		ret = vhost_blk_req_handle(vq, &hdr, head, out + in);
+		ret = vhost_blk_req_handle(vq, &hdr, head, out, in);
 		if (ret == -EAGAIN || ret == -ENOMEM) {
 			vhost_discard_vq_desc(vq, 1);
 			vhost_poll_queue(&vq->poll);
@@ -610,7 +613,12 @@ static void vhost_blk_handle_host_kick(struct vhost_work *work)
 		if (vhost_blk_set_status(req, status)) {
 			vhostblk_vq_err(blk, vq, "Failed to write status");
 		} else {
-			vhost_add_used(vq, req->head, req->len);
+			int used_len = sizeof(status);
+
+			if (req->bi_opf == REQ_OP_READ)
+				used_len += req->len;
+
+			vhost_add_used(vq, req->head, used_len);
 			added = true;
 		}
 
-- 
2.43.5


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH VZ10 v3 3/5] drivers/vhost/blk: fix flush support
  2026-08-25 12:51 [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup Andrey Zhadchenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 1/5] drivers/vhost/blk: harden get_id command Andrey Zhadchenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 2/5] drivers/vhost/blk: report correct used-ring lengths Andrey Zhadchenko
@ 2026-08-25 12:51 ` Andrey Zhadchenko
  2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 4/5] drivers/vhost/blk: fix sector alignment calculation Andrey Zhadchenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup Andrey Zhadchenko
  4 siblings, 1 reply; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 12:51 UTC (permalink / raw)


REQ_OP_FLUSH is a technical flag for requests. For bios the
correct combination would be REQ_OP_WRITE | REQ_PREFLUSH.
See an example in blkdev_issue_flush().

https://virtuozzo.atlassian.net/browse/VSTOR-138640
Fixes: 40a5928ec730 ("drivers/vhost: vhost-blk accelerator for virtio-blk guests")
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index 2c41a073004bc..5c8d5b210c315 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -274,7 +274,7 @@ static int vhost_blk_bio_make(struct vhost_blk_req *req,
 	sector_t sector = req->sector;
 	unsigned long pos = 0;
 
-	if (unlikely(req->bi_opf == REQ_OP_FLUSH))
+	if (unlikely(req->bi_opf & REQ_PREFLUSH))
 		return vhost_blk_bio_make_simple(req, bdev);
 
 	if (req->bi_opf == REQ_OP_WRITE && req->len & SECTOR_MASK) {
@@ -488,7 +488,7 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 		ret = vhost_blk_req_submit(req);
 		break;
 	case VIRTIO_BLK_T_FLUSH:
-		req->bi_opf = REQ_OP_FLUSH;
+		req->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
 		ret = vhost_blk_req_submit(req);
 		break;
 	case VIRTIO_BLK_T_GET_ID:
-- 
2.43.5


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH VZ10 v3 4/5] drivers/vhost/blk: fix sector alignment calculation
  2026-08-25 12:51 [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup Andrey Zhadchenko
                   ` (2 preceding siblings ...)
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 3/5] drivers/vhost/blk: fix flush support Andrey Zhadchenko
@ 2026-08-25 12:51 ` Andrey Zhadchenko
  2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup Andrey Zhadchenko
  4 siblings, 1 reply; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 12:51 UTC (permalink / raw)


SECTOR_MASK is actually a number 7, not some bitmask. Hence
the code actually checked 8 byte alignment.
Use IS_ALIGNED to correctly check sector alignment.

https://virtuozzo.atlassian.net/browse/VSTOR-138640
Fixes: a0d3b8956fb0 ("vhost-blk: rework iov and bio handling")
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---
v2: change (val & (SECTOR_SIZE - 1)) to !IS_ALIGNED(val, SECTOR_SIZE)

 drivers/vhost/blk.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index 5c8d5b210c315..b2cf111bbb455 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -209,13 +209,13 @@ inline static bool vhost_blk_iov_need_bb(struct vhost_blk_req *req)
 {
 	int i;
 
-	if (req->len & SECTOR_MASK)
+	if (!IS_ALIGNED(req->len, SECTOR_SIZE))
 		return true;
 
 	for (i = 0; i < req->iov_nr; i++) {
-		if (((size_t)req->iov[i].iov_base) & SECTOR_MASK)
+		if (!IS_ALIGNED((size_t)req->iov[i].iov_base, SECTOR_SIZE))
 			return true;
-		if (req->iov[i].iov_len & SECTOR_MASK)
+		if (!IS_ALIGNED(req->iov[i].iov_len, SECTOR_SIZE))
 			return true;
 	}
 
@@ -277,7 +277,7 @@ static int vhost_blk_bio_make(struct vhost_blk_req *req,
 	if (unlikely(req->bi_opf & REQ_PREFLUSH))
 		return vhost_blk_bio_make_simple(req, bdev);
 
-	if (req->bi_opf == REQ_OP_WRITE && req->len & SECTOR_MASK) {
+	if (req->bi_opf == REQ_OP_WRITE && !IS_ALIGNED(req->len, SECTOR_SIZE)) {
 		WARN_ONCE(1, "vhost-blk: write requests with unaligned len"
 			  " are not supported, len = %zu", req->len);
 		return -EINVAL;
@@ -339,7 +339,7 @@ static int vhost_blk_bio_make(struct vhost_blk_req *req,
 	nr_pages = bio_iov_vecs_to_alloc(&iter, BIO_MAX_VECS);
 	do {
 		/* We can't handle next bio if it's start is not sector aligned */
-		if (pos & SECTOR_MASK) {
+		if (!IS_ALIGNED(pos, SECTOR_SIZE)) {
 			WARN_ONCE(1, "vhost-blk: guest provided unaligned buffers");
 			ret = -EINVAL;
 			goto err_bio;
-- 
2.43.5


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup
  2026-08-25 12:51 [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup Andrey Zhadchenko
                   ` (3 preceding siblings ...)
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 4/5] drivers/vhost/blk: fix sector alignment calculation Andrey Zhadchenko
@ 2026-08-25 12:51 ` Andrey Zhadchenko
  2026-08-25 13:22   ` Konstantin Khorenko
                     ` (2 more replies)
  4 siblings, 3 replies; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 12:51 UTC (permalink / raw)


vhost_blk_setup() is pretty bad: silently refusing changed vq->num
if requests are already allocated, fetching user input second time
(double-fetch vulnerability).
To handle this, tie request allocation to backend existence. After
all, if there is no backend, there is no point in having requests.
Also expand it to get rid of boilerplate drop_backend, flush,
fput sequence in a few places.

https://virtuozzo.atlassian.net/browse/VSTOR-138640
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 126 +++++++++++++++++++-------------------------
 1 file changed, 54 insertions(+), 72 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index b2cf111bbb455..8e3025e934445 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -658,11 +658,14 @@ static void vhost_blk_flush(struct vhost_blk *blk)
 	spin_unlock(&blk->flush_lock);
 }
 
-static inline void vhost_blk_drop_backends(struct vhost_blk *blk)
+static void vhost_blk_drop_backend(struct vhost_blk *blk)
 {
 	struct vhost_virtqueue *vq;
 	int i;
 
+	if (!blk->backend)
+		return;
+
 	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
 		vq = &blk->vqs[i].vq;
 
@@ -670,6 +673,44 @@ static inline void vhost_blk_drop_backends(struct vhost_blk *blk)
 		vhost_vq_set_backend(vq, NULL);
 		mutex_unlock(&vq->mutex);
 	}
+
+	vhost_blk_flush(blk);
+
+	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
+		kvfree(blk->vqs[i].req);
+		blk->vqs[i].req = NULL;
+	}
+
+	fput(blk->backend);
+	blk->backend = NULL;
+}
+
+static int vhost_blk_setup_vqs(struct vhost_blk *blk)
+{
+	struct vhost_virtqueue *vq;
+	int i;
+
+	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
+		vq = &blk->vqs[i].vq;
+
+		if (!vhost_vq_is_setup(vq))
+			continue;
+
+		blk->vqs[i].req = kvmalloc_array(vq->num, sizeof(struct vhost_blk_req),
+						 GFP_KERNEL);
+		if (!blk->vqs[i].req)
+			return -ENOMEM;
+
+		mutex_lock(&vq->mutex);
+		vhost_vq_set_backend(vq, blk->backend);
+		if (vhost_vq_init_access(vq)) {
+			mutex_unlock(&vq->mutex);
+			return -EFAULT;
+		}
+		mutex_unlock(&vq->mutex);
+	}
+
+	return 0;
 }
 
 static int vhost_blk_open(struct inode *inode, struct file *file)
@@ -722,16 +763,10 @@ static int vhost_blk_open(struct inode *inode, struct file *file)
 static int vhost_blk_release(struct inode *inode, struct file *f)
 {
 	struct vhost_blk *blk = f->private_data;
-	int i;
 
-	vhost_blk_drop_backends(blk);
-	vhost_blk_flush(blk);
+	vhost_blk_drop_backend(blk);
 	vhost_dev_stop(&blk->dev);
-	if (blk->backend)
-		fput(blk->backend);
 	vhost_dev_cleanup(&blk->dev);
-	for (i = 0; i < VHOST_BLK_VQ_MAX; i++)
-		kvfree(blk->vqs[i].req);
 	kfree(blk->dev.vqs);
 	kvfree(blk);
 
@@ -765,32 +800,19 @@ static int vhost_blk_set_features(struct vhost_blk *blk, u64 features)
 
 static long vhost_blk_set_backend(struct vhost_blk *blk, int fd)
 {
-	struct vhost_virtqueue *vq;
 	struct file *file;
 	struct inode *inode;
-	int ret, i;
+	int ret;
 
 	mutex_lock(&blk->dev.mutex);
 	ret = vhost_dev_check_owner(&blk->dev);
 	if (ret)
 		goto out_dev;
 
-	/*
-	 * fd < 0 means "stop the device".  Detach the backend from every vq so
-	 * vhost_blk_handle_guest_kick() stops fetching descriptors, drain the
-	 * in-flight requests, and release the backing file.
-	 */
+	/* fd < 0 means "stop the device" */
 	if (fd < 0) {
-		if (!blk->backend) {
-			ret = 0;		/* already stopped */
-			goto out_dev;
-		}
-		vhost_blk_drop_backends(blk);
-		vhost_blk_flush(blk);
-		fput(blk->backend);
-		blk->backend = NULL;
 		ret = 0;
-		goto out_dev;
+		goto out_drop;
 	}
 
 	if (blk->backend) {
@@ -807,31 +829,20 @@ static long vhost_blk_set_backend(struct vhost_blk *blk, int fd)
 	inode = file->f_mapping->host;
 	if (!S_ISBLK(inode->i_mode)) {
 		ret = -EFAULT;
-		goto out_file;
-	}
-
-	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
-		vq = &blk->vqs[i].vq;
-		if (!vhost_vq_access_ok(vq)) {
-			ret = -EFAULT;
-			goto out_drop;
-		}
-
-		mutex_lock(&vq->mutex);
-		vhost_vq_set_backend(vq, file);
-		ret = vhost_vq_init_access(vq);
-		mutex_unlock(&vq->mutex);
+		fput(file);
+		goto out_dev;
 	}
 
 	blk->backend = file;
+	ret = vhost_blk_setup_vqs(blk);
+	if (ret)
+		goto out_drop;
 
 	mutex_unlock(&blk->dev.mutex);
 	return 0;
 
 out_drop:
-	vhost_blk_drop_backends(blk);
-out_file:
-	fput(file);
+	vhost_blk_drop_backend(blk);
 out_dev:
 	mutex_unlock(&blk->dev.mutex);
 	return ret;
@@ -840,7 +851,7 @@ static long vhost_blk_set_backend(struct vhost_blk *blk, int fd)
 static long vhost_blk_reset_owner(struct vhost_blk *blk)
 {
 	struct vhost_iotlb *umem;
-	int err, i;
+	int err;
 
 	mutex_lock(&blk->dev.mutex);
 	err = vhost_dev_check_owner(&blk->dev);
@@ -851,42 +862,15 @@ static long vhost_blk_reset_owner(struct vhost_blk *blk)
 		err = -ENOMEM;
 		goto done;
 	}
-	vhost_blk_drop_backends(blk);
-	if (blk->backend) {
-		fput(blk->backend);
-		blk->backend = NULL;
-	}
-	vhost_blk_flush(blk);
+	vhost_blk_drop_backend(blk);
 	vhost_dev_stop(&blk->dev);
 	vhost_dev_reset_owner(&blk->dev, umem);
 
-	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
-		kvfree(blk->vqs[i].req);
-		blk->vqs[i].req = NULL;
-	}
-
 done:
 	mutex_unlock(&blk->dev.mutex);
 	return err;
 }
 
-static int vhost_blk_setup(struct vhost_blk *blk, void __user *argp)
-{
-	struct vhost_vring_state s;
-
-	if (copy_from_user(&s, argp, sizeof(s)))
-		return -EFAULT;
-
-	if (blk->vqs[s.index].req)
-		return 0;
-
-	blk->vqs[s.index].req = kvmalloc(sizeof(struct vhost_blk_req) * s.num, GFP_KERNEL);
-	if (!blk->vqs[s.index].req)
-		return -ENOMEM;
-
-	return 0;
-}
-
 static long vhost_blk_ioctl(struct file *f, unsigned int ioctl,
 			    unsigned long arg)
 {
@@ -924,8 +908,6 @@ static long vhost_blk_ioctl(struct file *f, unsigned int ioctl,
 		ret = vhost_dev_ioctl(&blk->dev, ioctl, argp);
 		if (ret == -ENOIOCTLCMD)
 			ret = vhost_vring_ioctl(&blk->dev, ioctl, argp);
-		if (!ret && ioctl == VHOST_SET_VRING_NUM)
-			ret = vhost_blk_setup(blk, argp);
 		vhost_blk_flush(blk);
 		mutex_unlock(&blk->dev.mutex);
 		return ret;
-- 
2.43.5


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup Andrey Zhadchenko
@ 2026-08-25 13:22   ` Konstantin Khorenko
  2026-08-25 13:27     ` Andrey Zhadchenko
  2026-08-25 13:42   ` Konstantin Khorenko
  2026-08-25 13:57   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
  2 siblings, 1 reply; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:22 UTC (permalink / raw)


On 8/25/26 14:51, Andrey Zhadchenko wrote:
> vhost_blk_setup() is pretty bad: silently refusing changed vq->num
> if requests are already allocated, fetching user input second time
> (double-fetch vulnerability).
> To handle this, tie request allocation to backend existence. After
> all, if there is no backend, there is no point in having requests.
> Also expand it to get rid of boilerplate drop_backend, flush,
> fput sequence in a few places.
> 
> https://virtuozzo.atlassian.net/browse/VSTOR-138640
> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
> ---
...

> +static int vhost_blk_setup_vqs(struct vhost_blk *blk)
> +{
> +	struct vhost_virtqueue *vq;
> +	int i;
> +
> +	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
> +		vq = &blk->vqs[i].vq;
> +
> +		if (!vhost_vq_is_setup(vq))
> +			continue;
> +
> +		blk->vqs[i].req = kvmalloc_array(vq->num, sizeof(struct vhost_blk_req),
> +						 GFP_KERNEL);
> +		if (!blk->vqs[i].req)
> +			return -ENOMEM;
> +
> +		mutex_lock(&vq->mutex);
> +		vhost_vq_set_backend(vq, blk->backend);
> +		if (vhost_vq_init_access(vq)) {
> +			mutex_unlock(&vq->mutex);
> +			return -EFAULT;
> +		}
> +		mutex_unlock(&vq->mutex);

Well, i agree that currently vhost_vq_init_access() can return -EFAULT only as an error,
but may be it's still worth to write a more generic return ret code?

        int ret, i;

        for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
...
                mutex_lock(&vq->mutex);
                vhost_vq_set_backend(vq, blk->backend);
                ret = vhost_vq_init_access(vq);
                mutex_unlock(&vq->mutex);
                if (ret)
                        return ret;



> +	}
> +
> +	return 0;
>  }
>...

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup
  2026-08-25 13:22   ` Konstantin Khorenko
@ 2026-08-25 13:27     ` Andrey Zhadchenko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrey Zhadchenko @ 2026-08-25 13:27 UTC (permalink / raw)




On 8/25/26 15:22, Konstantin Khorenko wrote:
> On 8/25/26 14:51, Andrey Zhadchenko wrote:
>> vhost_blk_setup() is pretty bad: silently refusing changed vq->num
>> if requests are already allocated, fetching user input second time
>> (double-fetch vulnerability).
>> To handle this, tie request allocation to backend existence. After
>> all, if there is no backend, there is no point in having requests.
>> Also expand it to get rid of boilerplate drop_backend, flush,
>> fput sequence in a few places.
>>
>> https://virtuozzo.atlassian.net/browse/VSTOR-138640
>> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
>> ---
> ...
> 
>> +static int vhost_blk_setup_vqs(struct vhost_blk *blk)
>> +{
>> +	struct vhost_virtqueue *vq;
>> +	int i;
>> +
>> +	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
>> +		vq = &blk->vqs[i].vq;
>> +
>> +		if (!vhost_vq_is_setup(vq))
>> +			continue;
>> +
>> +		blk->vqs[i].req = kvmalloc_array(vq->num, sizeof(struct vhost_blk_req),
>> +						 GFP_KERNEL);
>> +		if (!blk->vqs[i].req)
>> +			return -ENOMEM;
>> +
>> +		mutex_lock(&vq->mutex);
>> +		vhost_vq_set_backend(vq, blk->backend);
>> +		if (vhost_vq_init_access(vq)) {
>> +			mutex_unlock(&vq->mutex);
>> +			return -EFAULT;
>> +		}
>> +		mutex_unlock(&vq->mutex);
> 
> Well, i agree that currently vhost_vq_init_access() can return -EFAULT only as an error,
> but may be it's still worth to write a more generic return ret code?

LGTM.
Original code was using this aprroach too, no idea why have I changed that.
Can you apply this in-place or do you want me to re-spin the series?

> 
>          int ret, i;
> 
>          for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
> ...
>                  mutex_lock(&vq->mutex);
>                  vhost_vq_set_backend(vq, blk->backend);
>                  ret = vhost_vq_init_access(vq);
>                  mutex_unlock(&vq->mutex);
>                  if (ret)
>                          return ret;
> 
> 
> 
>> +	}
>> +
>> +	return 0;
>>   }
>> ...


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup Andrey Zhadchenko
  2026-08-25 13:22   ` Konstantin Khorenko
@ 2026-08-25 13:42   ` Konstantin Khorenko
  2026-08-25 13:57   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
  2 siblings, 0 replies; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:42 UTC (permalink / raw)


A side finding, unrelated to this patch, but still, please fix it in a separate patch:

  In drivers/vhost/vhost.h the backend access rules are documented explicitly (drivers/vhost/vhost.h:288-310):

  /**
   * vhost_vq_set_backend - Set backend.
   * ...
   * Context: Need to call with vq->mutex acquired.
   */

  /**
   * vhost_vq_get_backend - Get backend.
   * ...
   * Context: Need to call with vq->mutex acquired.
   */

  So vq->private_data is a plain field with no barriers whatsoever - its consistency is guaranteed solely by both sides
  holding vq->mutex. Every other vhost driver honors that: handle_tx/handle_rx in vhost-net take
  mutex_lock_nested(&vq->mutex, ...) (drivers/vhost/net.c:948, 1117), vsock and scsi do the same in their kick paths. Only
  vhost_blk_handle_guest_kick() calls vhost_vq_get_backend() and then works with the ring without taking the mutex at all.

  Where exactly the race is

  The writer is vhost_blk_setup_vqs() (ioctl thread, under dev.mutex); the reader is the vhost worker (a separate kernel
  thread, no mutex). Side by side:

  CPU0: ioctl VHOST_BLK_SET_BACKEND             CPU1: vhost worker
        vhost_blk_setup_vqs()                         vhost_blk_handle_guest_kick()
  ------------------------------------------    ------------------------------------------
  blk->vqs[i].req = kvmalloc_array(...);
                      /* store 1: req array */
  mutex_lock(&vq->mutex);
  vhost_vq_set_backend(vq, blk->backend);
                      /* store 2: publish
                         the backend */
  mutex_unlock(&vq->mutex);
                                                f = vhost_vq_get_backend(vq);
                                                                  /* load 1: backend */
                                                if (!f)
                                                        return;
                                                ...
                                                head = vhost_get_vq_desc(...);
                                                ...
                                                req = &blk_vq->req[head];
                                                                  /* load 2: req array
                                                                     pointer, then
                                                                     dereference */

  Correctness rests on the invariant: if the reader observes backend != NULL, it must also observe the already-written
  blk_vq->req pointer. For that to hold, two things are required: (a) the writer's stores must become visible in program
  order (req first, backend second), and (b) the reader's loads must be performed in program order (backend first, req
  second).

  The writer-side mutex does not help here: a lock orders memory only between critical sections of that same lock. The
  reader never takes it, so there is no happens-before edge between the writer's mutex_unlock() and the worker's loads.
  Worse, mutex_lock() is an acquire operation, and acquire is one-directional: it forbids later accesses from moving up
  above it, but it does not forbid an earlier store (the req write) from sinking down below it. So even within the writer,
  store 1 and store 2 are formally unordered with respect to each other as far as an external observer is concerned.

  The failure interleaving on a weakly ordered CPU (e.g. arm64) then looks like this:

  CPU0                                          CPU1
  ------------------------------------------    ------------------------------------------
  store 2 becomes globally visible
     (backend != NULL)
                                                load 1: sees the new backend -> proceeds
                                                load 2: sees the *old* blk_vq->req
                                                        (NULL on first SET_BACKEND, or a
                                                         freed pointer after a -1/fd cycle)
                                                req = &NULL[head]   -> NULL-offset oops
                                                (or use-after-free)
  store 1 becomes globally visible
     (req array pointer) - too late

  On x86-64 this cannot happen, because TSO forbids both store-store reordering on CPU0 and load-load reordering on CPU1 -
  which is the only reason the current code works in practice.


Suggested fix:

  --- a/drivers/vhost/blk.c
  +++ b/drivers/vhost/blk.c
  @@ static void vhost_blk_handle_guest_kick(struct vhost_work *work)
        vq = container_of(work, struct vhost_virtqueue, poll.work);
        blk = container_of(vq->dev, struct vhost_blk, dev);
        blk_vq = container_of(vq, struct vhost_blk_vq, vq);

  +     mutex_lock(&vq->mutex);
  +
        f = vhost_vq_get_backend(vq);
        if (!f)
  -             return;
  +             goto out;

        vhost_disable_notify(&blk->dev, vq);
        for (;;) {
                head = vhost_get_vq_desc(vq, vq->iov,
                                         ARRAY_SIZE(vq->iov),
                                         &out, &in, NULL, NULL);
  @@
                if (!llist_empty(&blk_vq->llhead)) {
                        vhost_poll_queue(&vq->poll);
                        break;
                }
        }
  +out:
  +     mutex_unlock(&vq->mutex);
   }
  @@ static void vhost_blk_handle_host_kick(struct vhost_work *work)
        blk_vq = container_of(work, struct vhost_blk_vq, work);
        vq = &blk_vq->vq;
  +
  +     mutex_lock(&vq->mutex);
        llnode = llist_del_all(&blk_vq->llhead);
        added = false;
        while (llnode) {
  @@
                forget_request(req);
        }

        if (likely(added))
                vhost_signal(&blk->dev, vq);
  +     mutex_unlock(&vq->mutex);
   }



--
Best regards,

Konstantin Khorenko,
Virtuozzo Linux Kernel Team



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH RHEL10 COMMIT] drivers/vhost/blk: harden get_id command
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 1/5] drivers/vhost/blk: harden get_id command Andrey Zhadchenko
@ 2026-08-25 13:56   ` Konstantin Khorenko
  0 siblings, 0 replies; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:56 UTC (permalink / raw)


The commit is pushed to "branch-rh10-6.12.0-211.39.1.16.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.39.1.16.9.vz10
------>
commit 052898980e0330e6cc936ebf0f937d2431cdee61
Author: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
Date:   Tue Aug 25 15:51:48 2026 +0300

    drivers/vhost/blk: harden get_id command
    
    A defensive patch.
    QEMU is fine with reporting empty serial and including null
    terminator. So we will do the same.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-138640
    Feature: vhost-blk: in-kernel accelerator for virtio-blk guests
    Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
    Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
    Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index dac03566bfca5..edd3e75873ff5 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -105,8 +105,6 @@ struct vhost_blk {
 	pid_t owner_pid;
 };
 
-static int gen;
-
 static int move_iovec(struct iovec *from, struct iovec *to,
 		      size_t len, int iov_count_from, int iov_count_to)
 {
@@ -491,7 +489,9 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 		ret = vhost_blk_req_submit(req);
 		break;
 	case VIRTIO_BLK_T_GET_ID:
-		len = strnlen(blk->serial, VIRTIO_BLK_ID_BYTES);
+		len = min_t(size_t,
+			    strnlen(blk->serial, VIRTIO_BLK_ID_BYTES) + 1,
+			    min_t(size_t, req->len, VIRTIO_BLK_ID_BYTES));
 		iov_iter_init(&iter, ITER_DEST, req->iov, req->iov_nr, req->len);
 		ret = copy_to_iter(blk->serial, len, &iter);
 		status = ret != len ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
@@ -688,7 +688,6 @@ static int vhost_blk_open(struct inode *inode, struct file *file)
 	}
 
 	memset(blk->serial, 0, sizeof(blk->serial));
-	snprintf(blk->serial, VIRTIO_BLK_ID_BYTES, "vhost-blk%d", gen++);
 
 	atomic_set(&blk->req_inflight[0], 0);
 	atomic_set(&blk->req_inflight[1], 0);

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH RHEL10 COMMIT] drivers/vhost/blk: report correct used-ring lengths
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 2/5] drivers/vhost/blk: report correct used-ring lengths Andrey Zhadchenko
@ 2026-08-25 13:56   ` Konstantin Khorenko
  0 siblings, 0 replies; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:56 UTC (permalink / raw)


The commit is pushed to "branch-rh10-6.12.0-211.39.1.16.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.39.1.16.9.vz10
------>
commit 35b08c53bf488c7991dba12f966d3030d1328d4f
Author: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
Date:   Tue Aug 25 15:51:49 2026 +0300

    drivers/vhost/blk: report correct used-ring lengths
    
    We are expected to return amount of bytes written to the guest,
    which also includes status.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-138640
    Fixes: d8722ff88c5d ("drivers/vhost: vhost-blk accelerator for virtio-blk guests")
    Feature: vhost-blk: in-kernel accelerator for virtio-blk guests
    Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
    Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
    Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index edd3e75873ff5..2c41a073004bc 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -445,12 +445,14 @@ static int vhost_blk_req_submit(struct vhost_blk_req *req)
 
 static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 				struct virtio_blk_outhdr *hdr,
-				u16 head, u16 total_iov_nr)
+				u16 head, u16 out, u16 in)
 {
 	struct vhost_blk *blk = container_of(vq->dev, struct vhost_blk, dev);
 	struct vhost_blk_vq *blk_vq = container_of(vq, struct vhost_blk_vq, vq);
 	struct vhost_blk_req *req;
+	u16 total_iov_nr = out + in;
 	struct iov_iter iter;
+	size_t in_len;
 	int ret, len;
 	u8 status;
 
@@ -461,8 +463,9 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 	req->sector	= hdr->sector;
 	req->iov	= blk_vq->iov;
 	req->bio_err	= 0;
+	in_len		= iov_length(vq->iov + out, in);
 
-	if (iov_length(vq->iov, total_iov_nr) < sizeof(status))
+	if (in_len < sizeof(status) || in_len > INT_MAX)
 		return -EINVAL;
 
 	req->len	= iov_length(vq->iov, total_iov_nr) - sizeof(status);
@@ -498,14 +501,14 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 		ret = vhost_blk_set_status(req, status);
 		if (ret)
 			break;
-		vhost_add_used_and_signal(&blk->dev, vq, head, len);
+		vhost_add_used_and_signal(&blk->dev, vq, head, in_len);
 		break;
 	default:
 		status = VIRTIO_BLK_S_UNSUPP;
 		ret = vhost_blk_set_status(req, status);
 		if (ret)
 			break;
-		vhost_add_used_and_signal(&blk->dev, vq, head, 0);
+		vhost_add_used_and_signal(&blk->dev, vq, head, sizeof(status));
 	}
 
 	return ret;
@@ -561,7 +564,7 @@ static void vhost_blk_handle_guest_kick(struct vhost_work *work)
 			break;
 		}
 
-		ret = vhost_blk_req_handle(vq, &hdr, head, out + in);
+		ret = vhost_blk_req_handle(vq, &hdr, head, out, in);
 		if (ret == -EAGAIN || ret == -ENOMEM) {
 			vhost_discard_vq_desc(vq, 1);
 			vhost_poll_queue(&vq->poll);
@@ -610,7 +613,12 @@ static void vhost_blk_handle_host_kick(struct vhost_work *work)
 		if (vhost_blk_set_status(req, status)) {
 			vhostblk_vq_err(blk, vq, "Failed to write status");
 		} else {
-			vhost_add_used(vq, req->head, req->len);
+			int used_len = sizeof(status);
+
+			if (req->bi_opf == REQ_OP_READ)
+				used_len += req->len;
+
+			vhost_add_used(vq, req->head, used_len);
 			added = true;
 		}
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH RHEL10 COMMIT] drivers/vhost/blk: fix flush support
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 3/5] drivers/vhost/blk: fix flush support Andrey Zhadchenko
@ 2026-08-25 13:56   ` Konstantin Khorenko
  0 siblings, 0 replies; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:56 UTC (permalink / raw)


The commit is pushed to "branch-rh10-6.12.0-211.39.1.16.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.39.1.16.9.vz10
------>
commit bc3b45a6f06df2bcd7ef709f5998b10b2bdd5612
Author: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
Date:   Tue Aug 25 15:51:50 2026 +0300

    drivers/vhost/blk: fix flush support
    
    REQ_OP_FLUSH is a technical flag for requests. For bios the
    correct combination would be REQ_OP_WRITE | REQ_PREFLUSH.
    See an example in blkdev_issue_flush().
    
    https://virtuozzo.atlassian.net/browse/VSTOR-138640
    Fixes: d8722ff88c5d ("drivers/vhost: vhost-blk accelerator for virtio-blk guests")
    Feature: vhost-blk: in-kernel accelerator for virtio-blk guests
    Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
    Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
    Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index 2c41a073004bc..5c8d5b210c315 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -274,7 +274,7 @@ static int vhost_blk_bio_make(struct vhost_blk_req *req,
 	sector_t sector = req->sector;
 	unsigned long pos = 0;
 
-	if (unlikely(req->bi_opf == REQ_OP_FLUSH))
+	if (unlikely(req->bi_opf & REQ_PREFLUSH))
 		return vhost_blk_bio_make_simple(req, bdev);
 
 	if (req->bi_opf == REQ_OP_WRITE && req->len & SECTOR_MASK) {
@@ -488,7 +488,7 @@ static int vhost_blk_req_handle(struct vhost_virtqueue *vq,
 		ret = vhost_blk_req_submit(req);
 		break;
 	case VIRTIO_BLK_T_FLUSH:
-		req->bi_opf = REQ_OP_FLUSH;
+		req->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
 		ret = vhost_blk_req_submit(req);
 		break;
 	case VIRTIO_BLK_T_GET_ID:

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH RHEL10 COMMIT] drivers/vhost/blk: fix sector alignment calculation
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 4/5] drivers/vhost/blk: fix sector alignment calculation Andrey Zhadchenko
@ 2026-08-25 13:56   ` Konstantin Khorenko
  0 siblings, 0 replies; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:56 UTC (permalink / raw)


The commit is pushed to "branch-rh10-6.12.0-211.39.1.16.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.39.1.16.9.vz10
------>
commit 0aeb1139bbf38fa05d4b1f48df906c502f99c121
Author: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
Date:   Tue Aug 25 15:51:51 2026 +0300

    drivers/vhost/blk: fix sector alignment calculation
    
    SECTOR_MASK is actually a number 7, not some bitmask. Hence
    the code actually checked 8 byte alignment.
    Use IS_ALIGNED to correctly check sector alignment.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-138640
    Fixes: a0d3b8956fb0 ("vhost-blk: rework iov and bio handling")
    Fixes: ca8ed3fe5da5 ("vhost-blk: add bounce-buffer for non-aligned requests")
    Feature: vhost-blk: in-kernel accelerator for virtio-blk guests
    Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
    Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
    Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index 5c8d5b210c315..b2cf111bbb455 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -209,13 +209,13 @@ inline static bool vhost_blk_iov_need_bb(struct vhost_blk_req *req)
 {
 	int i;
 
-	if (req->len & SECTOR_MASK)
+	if (!IS_ALIGNED(req->len, SECTOR_SIZE))
 		return true;
 
 	for (i = 0; i < req->iov_nr; i++) {
-		if (((size_t)req->iov[i].iov_base) & SECTOR_MASK)
+		if (!IS_ALIGNED((size_t)req->iov[i].iov_base, SECTOR_SIZE))
 			return true;
-		if (req->iov[i].iov_len & SECTOR_MASK)
+		if (!IS_ALIGNED(req->iov[i].iov_len, SECTOR_SIZE))
 			return true;
 	}
 
@@ -277,7 +277,7 @@ static int vhost_blk_bio_make(struct vhost_blk_req *req,
 	if (unlikely(req->bi_opf & REQ_PREFLUSH))
 		return vhost_blk_bio_make_simple(req, bdev);
 
-	if (req->bi_opf == REQ_OP_WRITE && req->len & SECTOR_MASK) {
+	if (req->bi_opf == REQ_OP_WRITE && !IS_ALIGNED(req->len, SECTOR_SIZE)) {
 		WARN_ONCE(1, "vhost-blk: write requests with unaligned len"
 			  " are not supported, len = %zu", req->len);
 		return -EINVAL;
@@ -339,7 +339,7 @@ static int vhost_blk_bio_make(struct vhost_blk_req *req,
 	nr_pages = bio_iov_vecs_to_alloc(&iter, BIO_MAX_VECS);
 	do {
 		/* We can't handle next bio if it's start is not sector aligned */
-		if (pos & SECTOR_MASK) {
+		if (!IS_ALIGNED(pos, SECTOR_SIZE)) {
 			WARN_ONCE(1, "vhost-blk: guest provided unaligned buffers");
 			ret = -EINVAL;
 			goto err_bio;

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Devel] [PATCH RHEL10 COMMIT] drivers/vhost/blk: rework queue/backend setup
  2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup Andrey Zhadchenko
  2026-08-25 13:22   ` Konstantin Khorenko
  2026-08-25 13:42   ` Konstantin Khorenko
@ 2026-08-25 13:57   ` Konstantin Khorenko
  2 siblings, 0 replies; 14+ messages in thread
From: Konstantin Khorenko @ 2026-08-25 13:57 UTC (permalink / raw)


The commit is pushed to "branch-rh10-6.12.0-211.39.1.16.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.39.1.16.9.vz10
------>
commit 95d39b7ffa50047eadf53f48419e8cea84a175ea
Author: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
Date:   Tue Aug 25 15:51:52 2026 +0300

    drivers/vhost/blk: rework queue/backend setup
    
    vhost_blk_setup() is pretty bad: silently refusing changed vq->num
    if requests are already allocated, fetching user input second time
    (double-fetch vulnerability).
    To handle this, tie request allocation to backend existence. After
    all, if there is no backend, there is no point in having requests.
    Also expand it to get rid of boilerplate drop_backend, flush,
    fput sequence in a few places.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-138640
    Fixes: d8722ff88c5d ("drivers/vhost: vhost-blk accelerator for virtio-blk guests")
    Feature: vhost-blk: in-kernel accelerator for virtio-blk guests
    Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
    Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
    Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 drivers/vhost/blk.c | 125 ++++++++++++++++++++++------------------------------
 1 file changed, 53 insertions(+), 72 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index b2cf111bbb455..fbdd04f5a7e28 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -658,11 +658,14 @@ static void vhost_blk_flush(struct vhost_blk *blk)
 	spin_unlock(&blk->flush_lock);
 }
 
-static inline void vhost_blk_drop_backends(struct vhost_blk *blk)
+static void vhost_blk_drop_backend(struct vhost_blk *blk)
 {
 	struct vhost_virtqueue *vq;
 	int i;
 
+	if (!blk->backend)
+		return;
+
 	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
 		vq = &blk->vqs[i].vq;
 
@@ -670,6 +673,43 @@ static inline void vhost_blk_drop_backends(struct vhost_blk *blk)
 		vhost_vq_set_backend(vq, NULL);
 		mutex_unlock(&vq->mutex);
 	}
+
+	vhost_blk_flush(blk);
+
+	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
+		kvfree(blk->vqs[i].req);
+		blk->vqs[i].req = NULL;
+	}
+
+	fput(blk->backend);
+	blk->backend = NULL;
+}
+
+static int vhost_blk_setup_vqs(struct vhost_blk *blk)
+{
+	struct vhost_virtqueue *vq;
+	int ret, i;
+
+	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
+		vq = &blk->vqs[i].vq;
+
+		if (!vhost_vq_is_setup(vq))
+			continue;
+
+		blk->vqs[i].req = kvmalloc_array(vq->num, sizeof(struct vhost_blk_req),
+						 GFP_KERNEL);
+		if (!blk->vqs[i].req)
+			return -ENOMEM;
+
+		mutex_lock(&vq->mutex);
+		vhost_vq_set_backend(vq, blk->backend);
+		ret = vhost_vq_init_access(vq);
+		mutex_unlock(&vq->mutex);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
 }
 
 static int vhost_blk_open(struct inode *inode, struct file *file)
@@ -722,16 +762,10 @@ static int vhost_blk_open(struct inode *inode, struct file *file)
 static int vhost_blk_release(struct inode *inode, struct file *f)
 {
 	struct vhost_blk *blk = f->private_data;
-	int i;
 
-	vhost_blk_drop_backends(blk);
-	vhost_blk_flush(blk);
+	vhost_blk_drop_backend(blk);
 	vhost_dev_stop(&blk->dev);
-	if (blk->backend)
-		fput(blk->backend);
 	vhost_dev_cleanup(&blk->dev);
-	for (i = 0; i < VHOST_BLK_VQ_MAX; i++)
-		kvfree(blk->vqs[i].req);
 	kfree(blk->dev.vqs);
 	kvfree(blk);
 
@@ -765,32 +799,19 @@ static int vhost_blk_set_features(struct vhost_blk *blk, u64 features)
 
 static long vhost_blk_set_backend(struct vhost_blk *blk, int fd)
 {
-	struct vhost_virtqueue *vq;
 	struct file *file;
 	struct inode *inode;
-	int ret, i;
+	int ret;
 
 	mutex_lock(&blk->dev.mutex);
 	ret = vhost_dev_check_owner(&blk->dev);
 	if (ret)
 		goto out_dev;
 
-	/*
-	 * fd < 0 means "stop the device".  Detach the backend from every vq so
-	 * vhost_blk_handle_guest_kick() stops fetching descriptors, drain the
-	 * in-flight requests, and release the backing file.
-	 */
+	/* fd < 0 means "stop the device" */
 	if (fd < 0) {
-		if (!blk->backend) {
-			ret = 0;		/* already stopped */
-			goto out_dev;
-		}
-		vhost_blk_drop_backends(blk);
-		vhost_blk_flush(blk);
-		fput(blk->backend);
-		blk->backend = NULL;
 		ret = 0;
-		goto out_dev;
+		goto out_drop;
 	}
 
 	if (blk->backend) {
@@ -807,31 +828,20 @@ static long vhost_blk_set_backend(struct vhost_blk *blk, int fd)
 	inode = file->f_mapping->host;
 	if (!S_ISBLK(inode->i_mode)) {
 		ret = -EFAULT;
-		goto out_file;
-	}
-
-	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
-		vq = &blk->vqs[i].vq;
-		if (!vhost_vq_access_ok(vq)) {
-			ret = -EFAULT;
-			goto out_drop;
-		}
-
-		mutex_lock(&vq->mutex);
-		vhost_vq_set_backend(vq, file);
-		ret = vhost_vq_init_access(vq);
-		mutex_unlock(&vq->mutex);
+		fput(file);
+		goto out_dev;
 	}
 
 	blk->backend = file;
+	ret = vhost_blk_setup_vqs(blk);
+	if (ret)
+		goto out_drop;
 
 	mutex_unlock(&blk->dev.mutex);
 	return 0;
 
 out_drop:
-	vhost_blk_drop_backends(blk);
-out_file:
-	fput(file);
+	vhost_blk_drop_backend(blk);
 out_dev:
 	mutex_unlock(&blk->dev.mutex);
 	return ret;
@@ -840,7 +850,7 @@ static long vhost_blk_set_backend(struct vhost_blk *blk, int fd)
 static long vhost_blk_reset_owner(struct vhost_blk *blk)
 {
 	struct vhost_iotlb *umem;
-	int err, i;
+	int err;
 
 	mutex_lock(&blk->dev.mutex);
 	err = vhost_dev_check_owner(&blk->dev);
@@ -851,42 +861,15 @@ static long vhost_blk_reset_owner(struct vhost_blk *blk)
 		err = -ENOMEM;
 		goto done;
 	}
-	vhost_blk_drop_backends(blk);
-	if (blk->backend) {
-		fput(blk->backend);
-		blk->backend = NULL;
-	}
-	vhost_blk_flush(blk);
+	vhost_blk_drop_backend(blk);
 	vhost_dev_stop(&blk->dev);
 	vhost_dev_reset_owner(&blk->dev, umem);
 
-	for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
-		kvfree(blk->vqs[i].req);
-		blk->vqs[i].req = NULL;
-	}
-
 done:
 	mutex_unlock(&blk->dev.mutex);
 	return err;
 }
 
-static int vhost_blk_setup(struct vhost_blk *blk, void __user *argp)
-{
-	struct vhost_vring_state s;
-
-	if (copy_from_user(&s, argp, sizeof(s)))
-		return -EFAULT;
-
-	if (blk->vqs[s.index].req)
-		return 0;
-
-	blk->vqs[s.index].req = kvmalloc(sizeof(struct vhost_blk_req) * s.num, GFP_KERNEL);
-	if (!blk->vqs[s.index].req)
-		return -ENOMEM;
-
-	return 0;
-}
-
 static long vhost_blk_ioctl(struct file *f, unsigned int ioctl,
 			    unsigned long arg)
 {
@@ -924,8 +907,6 @@ static long vhost_blk_ioctl(struct file *f, unsigned int ioctl,
 		ret = vhost_dev_ioctl(&blk->dev, ioctl, argp);
 		if (ret == -ENOIOCTLCMD)
 			ret = vhost_vring_ioctl(&blk->dev, ioctl, argp);
-		if (!ret && ioctl == VHOST_SET_VRING_NUM)
-			ret = vhost_blk_setup(blk, argp);
 		vhost_blk_flush(blk);
 		mutex_unlock(&blk->dev.mutex);
 		return ret;

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-08-25 13:57 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-25 12:51 [Devel] [PATCH VZ10 v3 0/5] vhost-blk: fix protocol handling and backend setup Andrey Zhadchenko
2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 1/5] drivers/vhost/blk: harden get_id command Andrey Zhadchenko
2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 2/5] drivers/vhost/blk: report correct used-ring lengths Andrey Zhadchenko
2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 3/5] drivers/vhost/blk: fix flush support Andrey Zhadchenko
2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 4/5] drivers/vhost/blk: fix sector alignment calculation Andrey Zhadchenko
2026-08-25 13:56   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko
2026-08-25 12:51 ` [Devel] [PATCH VZ10 v3 5/5] drivers/vhost/blk: rework queue/backend setup Andrey Zhadchenko
2026-08-25 13:22   ` Konstantin Khorenko
2026-08-25 13:27     ` Andrey Zhadchenko
2026-08-25 13:42   ` Konstantin Khorenko
2026-08-25 13:57   ` [Devel] [PATCH RHEL10 COMMIT] " Konstantin Khorenko

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.