Patch "i2c: virtio: fix completion handling" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    i2c: virtio: fix completion handling

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     i2c-virtio-fix-completion-handling.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 9d1ce80141b5dab6c033df8048c51ef25661a3ce
Author: Vincent Whitchurch <vincent.whitchurch@xxxxxxxx>
Date:   Thu Dec 2 16:32:14 2021 +0100

    i2c: virtio: fix completion handling
    
    [ Upstream commit b503de239f62eca898cfb7e820d9a35499137d22 ]
    
    The driver currently assumes that the notify callback is only received
    when the device is done with all the queued buffers.
    
    However, this is not true, since the notify callback could be called
    without any of the queued buffers being completed (for example, with
    virtio-pci and shared interrupts) or with only some of the buffers being
    completed (since the driver makes them available to the device in
    multiple separate virtqueue_add_sgs() calls).
    
    This can lead to incorrect data on the I2C bus or memory corruption in
    the guest if the device operates on buffers which are have been freed by
    the driver.  (The WARN_ON in the driver is also triggered.)
    
     BUG kmalloc-128 (Tainted: G        W        ): Poison overwritten
     First byte 0x0 instead of 0x6b
     Allocated in i2cdev_ioctl_rdwr+0x9d/0x1de age=243 cpu=0 pid=28
            memdup_user+0x2e/0xbd
            i2cdev_ioctl_rdwr+0x9d/0x1de
            i2cdev_ioctl+0x247/0x2ed
            vfs_ioctl+0x21/0x30
            sys_ioctl+0xb18/0xb41
     Freed in i2cdev_ioctl_rdwr+0x1bb/0x1de age=68 cpu=0 pid=28
            kfree+0x1bd/0x1cc
            i2cdev_ioctl_rdwr+0x1bb/0x1de
            i2cdev_ioctl+0x247/0x2ed
            vfs_ioctl+0x21/0x30
            sys_ioctl+0xb18/0xb41
    
    Fix this by calling virtio_get_buf() from the notify handler like other
    virtio drivers and by actually waiting for all the buffers to be
    completed.
    
    Fixes: 3cfc88380413d20f ("i2c: virtio: add a virtio i2c frontend driver")
    Acked-by: Viresh Kumar <viresh.kumar@xxxxxxxxxx>
    Signed-off-by: Vincent Whitchurch <vincent.whitchurch@xxxxxxxx>
    Acked-by: Michael S. Tsirkin <mst@xxxxxxxxxx>
    Signed-off-by: Wolfram Sang <wsa@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c
index 7b2474e6876f4..5cb21d7da05b6 100644
--- a/drivers/i2c/busses/i2c-virtio.c
+++ b/drivers/i2c/busses/i2c-virtio.c
@@ -22,24 +22,24 @@
 /**
  * struct virtio_i2c - virtio I2C data
  * @vdev: virtio device for this controller
- * @completion: completion of virtio I2C message
  * @adap: I2C adapter for this controller
  * @vq: the virtio virtqueue for communication
  */
 struct virtio_i2c {
 	struct virtio_device *vdev;
-	struct completion completion;
 	struct i2c_adapter adap;
 	struct virtqueue *vq;
 };
 
 /**
  * struct virtio_i2c_req - the virtio I2C request structure
+ * @completion: completion of virtio I2C message
  * @out_hdr: the OUT header of the virtio I2C message
  * @buf: the buffer into which data is read, or from which it's written
  * @in_hdr: the IN header of the virtio I2C message
  */
 struct virtio_i2c_req {
+	struct completion completion;
 	struct virtio_i2c_out_hdr out_hdr	____cacheline_aligned;
 	uint8_t *buf				____cacheline_aligned;
 	struct virtio_i2c_in_hdr in_hdr		____cacheline_aligned;
@@ -47,9 +47,11 @@ struct virtio_i2c_req {
 
 static void virtio_i2c_msg_done(struct virtqueue *vq)
 {
-	struct virtio_i2c *vi = vq->vdev->priv;
+	struct virtio_i2c_req *req;
+	unsigned int len;
 
-	complete(&vi->completion);
+	while ((req = virtqueue_get_buf(vq, &len)))
+		complete(&req->completion);
 }
 
 static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
@@ -62,6 +64,8 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
 	for (i = 0; i < num; i++) {
 		int outcnt = 0, incnt = 0;
 
+		init_completion(&reqs[i].completion);
+
 		/*
 		 * We don't support 0 length messages and so filter out
 		 * 0 length transfers by using i2c_adapter_quirks.
@@ -108,21 +112,15 @@ static int virtio_i2c_complete_reqs(struct virtqueue *vq,
 				    struct virtio_i2c_req *reqs,
 				    struct i2c_msg *msgs, int num)
 {
-	struct virtio_i2c_req *req;
 	bool failed = false;
-	unsigned int len;
 	int i, j = 0;
 
 	for (i = 0; i < num; i++) {
-		/* Detach the ith request from the vq */
-		req = virtqueue_get_buf(vq, &len);
+		struct virtio_i2c_req *req = &reqs[i];
 
-		/*
-		 * Condition req == &reqs[i] should always meet since we have
-		 * total num requests in the vq. reqs[i] can never be NULL here.
-		 */
-		if (!failed && (WARN_ON(req != &reqs[i]) ||
-				req->in_hdr.status != VIRTIO_I2C_MSG_OK))
+		wait_for_completion(&req->completion);
+
+		if (!failed && req->in_hdr.status != VIRTIO_I2C_MSG_OK)
 			failed = true;
 
 		i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
@@ -158,12 +156,8 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 	 * remote here to clear the virtqueue, so we can try another set of
 	 * messages later on.
 	 */
-
-	reinit_completion(&vi->completion);
 	virtqueue_kick(vq);
 
-	wait_for_completion(&vi->completion);
-
 	count = virtio_i2c_complete_reqs(vq, reqs, msgs, count);
 
 err_free:
@@ -211,8 +205,6 @@ static int virtio_i2c_probe(struct virtio_device *vdev)
 	vdev->priv = vi;
 	vi->vdev = vdev;
 
-	init_completion(&vi->completion);
-
 	ret = virtio_i2c_setup_vqs(vi);
 	if (ret)
 		return ret;



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux