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
