virtio_i2c_complete_reqs() uses wait_for_completion_interruptible() and stops
waiting when a signal arrives. virtio_i2c_xfer() then frees reqs and the
per-request DMA bounce buffers while the device may still hold virtqueue tokens
pointing at &reqs[i] and DMA into read bounce buffers. Additionally, when the
device later completes those requests, virtio_i2c_msg_done() calls complete()
on freed memory and can corrupt the slab freelist.

Wait uninterruptibly for every completion before freeing reqs. This
matches how other virtio drivers retain request storage until the device
completes it. The virtio spec unfortunately does not provide a way to
cancel an in-flight request, so waiting uninterruptibly is required.

Signed-off-by: Gavin Li <[email protected]>
---
 drivers/i2c/busses/i2c-virtio.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c
index 5da6fef92bec3..12acc049f5999 100644
--- a/drivers/i2c/busses/i2c-virtio.c
+++ b/drivers/i2c/busses/i2c-virtio.c
@@ -116,14 +116,13 @@ static int virtio_i2c_complete_reqs(struct virtqueue *vq,
        for (i = 0; i < num; i++) {
                struct virtio_i2c_req *req = &reqs[i];
 
-               if (!failed) {
-                       if (wait_for_completion_interruptible(&req->completion))
-                               failed = true;
-                       else if (req->in_hdr.status != VIRTIO_I2C_MSG_OK)
-                               failed = true;
-                       else
-                               j++;
-               }
+               /* Wait uninterruptibly: device still owns token/bounce buf 
until completion. */
+               wait_for_completion(&req->completion);
+
+               if (!failed)
+                       failed = req->in_hdr.status != VIRTIO_I2C_MSG_OK;
+               if (!failed)
+                       j++;
 
                i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
        }
-- 
2.54.0


Reply via email to