[PATCH] nvme-host: tcp: do not read from the socket if we are performing a reset
Maurizio Lombardi
mlombard at redhat.com
Fri Apr 15 03:14:37 PDT 2022
nvme_tcp_io_work() may be scheduled while the controller is resetting,
triggering kernel crashes because of invalid memory accesses.
general protection fault, probably for non-canonical
address 0x82f4228d6e5c6
[exception RIP: _copy_to_iter+1344]
[ffffa8a9e5c07c90] __skb_datagram_iter at ffffffffb0c339d8
[ffffa8a9e5c07cf8] skb_copy_datagram_iter at ffffffffb0c33c83
[ffffa8a9e5c07d28] nvme_tcp_recv_data at ffffffffc04dbff7 [nvme_tcp]
[ffffa8a9e5c07d78] nvme_tcp_recv_skb at ffffffffc04dc90e [nvme_tcp]
[ffffa8a9e5c07dc0] tcp_read_sock at ffffffffb0cfedbe
[ffffa8a9e5c07e10] nvme_tcp_try_recv at ffffffffc04da518 [nvme_tcp]
[ffffa8a9e5c07e58] nvme_tcp_io_work at ffffffffc04dbc54 [nvme_tcp]
[ffffa8a9e5c07e88] process_one_work at ffffffffb0505408
[ffffa8a9e5c07ed0] worker_thread at ffffffffb05059a0
Fix this bug by preventing nvme_tcp_io_work() from running if the queue
is not flagged as "LIVE" and is not in the process of
connecting to the target.
Signed-off-by: Maurizio Lombardi <mlombard at redhat.com>
---
drivers/nvme/host/tcp.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index ad3a2bf2f1e9..e3ef014bbc0b 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -103,6 +103,7 @@ enum nvme_tcp_queue_flags {
NVME_TCP_Q_ALLOCATED = 0,
NVME_TCP_Q_LIVE = 1,
NVME_TCP_Q_POLLING = 2,
+ NVME_TCP_Q_CONNECTING = 3,
};
enum nvme_tcp_recv_state {
@@ -1213,6 +1214,10 @@ static void nvme_tcp_io_work(struct work_struct *w)
bool pending = false;
int result;
+ if (unlikely(!test_bit(NVME_TCP_Q_LIVE, &queue->flags) &&
+ !test_bit(NVME_TCP_Q_CONNECTING, &queue->flags)))
+ return;
+
if (mutex_trylock(&queue->send_mutex)) {
result = nvme_tcp_try_send(queue);
mutex_unlock(&queue->send_mutex);
@@ -1670,6 +1675,8 @@ static int nvme_tcp_start_queue(struct nvme_ctrl *nctrl, int idx)
struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
int ret;
+ set_bit(NVME_TCP_Q_CONNECTING, &ctrl->queues[idx].flags);
+
if (idx)
ret = nvmf_connect_io_queue(nctrl, idx);
else
@@ -1683,6 +1690,7 @@ static int nvme_tcp_start_queue(struct nvme_ctrl *nctrl, int idx)
dev_err(nctrl->device,
"failed to connect queue: %d ret=%d\n", idx, ret);
}
+ clear_bit(NVME_TCP_Q_CONNECTING, &ctrl->queues[idx].flags);
return ret;
}
--
2.27.0
More information about the Linux-nvme
mailing list