From b4d8353913a2f54a9bce314e84415e868dc096cb Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 20 Nov 2020 14:19:21 +0100 Subject: [KERNEL] Fix race condition for request_queuereceive in receive thread Formerly, the request that was about to be received was looked up in the receive queue without removing it, then the request payload was received from the socket while the lock was not being held, and finally, the lock was required again and the request removed from the queue. This is dangrous as another thread can concurrently take the request from the queue while the receive thread reads the payload from the socket, leading to a double-free by calling blk_mq_end_request twice. --- src/kernel/net.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/kernel/net.c b/src/kernel/net.c index d846b64..70695d9 100644 --- a/src/kernel/net.c +++ b/src/kernel/net.c @@ -795,6 +795,7 @@ static int dnbd3_net_receive(void *data) if ((uint64_t)(uintptr_t)received_request == dnbd3_reply.handle) // Double cast to prevent warning on 32bit { blk_request = received_request; + list_del_init(&blk_request->queuelist); break; } } @@ -813,30 +814,28 @@ static int dnbd3_net_receive(void *data) iov.iov_base = kaddr; iov.iov_len = bvec->bv_len; ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, bvec->bv_len, msg.msg_flags); + kunmap(bvec->bv_page); if (ret != bvec->bv_len) { - kunmap(bvec->bv_page); - if (ret == 0) { /* have not received any data, but remote peer is shutdown properly */ dnbd3_dev_dbg_host_cur(dev, "remote peer has performed an orderly shutdown\n"); ret = 0; - goto cleanup; } else { if (!atomic_read(&dev->connection_lock)) dnbd3_dev_err_host_cur(dev, "receiving from net to block layer\n"); ret = -EINVAL; - goto cleanup; } + // Requeue request + spin_lock_irqsave(&dev->blk_lock, irqflags); + list_add(&blk_request->queuelist, &dev->request_queue_send); + spin_unlock_irqrestore(&dev->blk_lock, irqflags); + goto cleanup; } - kunmap(bvec->bv_page); } - spin_lock_irqsave(&dev->blk_lock, irqflags); - list_del_init(&blk_request->queuelist); - spin_unlock_irqrestore(&dev->blk_lock, irqflags); blk_mq_end_request(blk_request, BLK_STS_OK); continue; -- cgit v1.2.3-55-g7522