diff options
author | Simon Rettberg | 2020-11-20 14:19:21 +0100 |
---|---|---|
committer | Simon Rettberg | 2020-11-20 14:19:21 +0100 |
commit | b4d8353913a2f54a9bce314e84415e868dc096cb (patch) | |
tree | 8712205f0b6b9970b1c197e8b9b4f86c52884798 | |
parent | [KERNEL] Cleanup thread cleanup, fix closing of device when busy (diff) | |
download | dnbd3-b4d8353913a2f54a9bce314e84415e868dc096cb.tar.gz dnbd3-b4d8353913a2f54a9bce314e84415e868dc096cb.tar.xz dnbd3-b4d8353913a2f54a9bce314e84415e868dc096cb.zip |
[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.
-rw-r--r-- | src/kernel/net.c | 15 |
1 files 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; |