Skip to content

Commit f965b28

Browse files
maurizio-lombardikeithbusch
authored andcommitted
nvmet-auth: complete a request only after freeing the dhchap pointers
It may happen that the work to destroy a queue (for example nvmet_tcp_release_queue_work()) is started while an auth-send or auth-receive command is still completing. nvmet_sq_destroy() will block, waiting for all the references to the sq to be dropped, the last reference is then dropped when nvmet_req_complete() is called. When this happens, both nvmet_sq_destroy() and nvmet_execute_auth_send()/_receive() will free the dhchap pointers by calling nvmet_auth_sq_free(). Since there isn't any lock, the two threads may race against each other, causing double frees and memory corruptions, as reported by KASAN. Reproduced by stress blktests nvme/041 nvme/042 nvme/043 nvme nvme2: qid 0: authenticated with hash hmac(sha512) dhgroup ffdhe4096 ================================================================== BUG: KASAN: double-free in kfree+0xec/0x4b0 Call Trace: <TASK> kfree+0xec/0x4b0 nvmet_auth_sq_free+0xe1/0x160 [nvmet] nvmet_execute_auth_send+0x482/0x16d0 [nvmet] process_one_work+0x8e5/0x1510 Allocated by task 191846: __kasan_kmalloc+0x81/0xa0 nvmet_auth_ctrl_sesskey+0xf6/0x380 [nvmet] nvmet_auth_reply+0x119/0x990 [nvmet] Freed by task 143270: kfree+0xec/0x4b0 nvmet_auth_sq_free+0xe1/0x160 [nvmet] process_one_work+0x8e5/0x1510 Fix this bug by calling nvmet_req_complete() only after freeing the pointers, so we will prevent the race by holding the sq reference. V2: remove redundant code Fixes: db1312d ("nvmet: implement basic In-Band Authentication") Signed-off-by: Maurizio Lombardi <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent 2b32c76 commit f965b28

File tree

1 file changed

+6
-3
lines changed

1 file changed

+6
-3
lines changed

drivers/nvme/target/fabrics-cmd-auth.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,21 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
333333
__func__, ctrl->cntlid, req->sq->qid,
334334
status, req->error_loc);
335335
req->cqe->result.u64 = 0;
336-
nvmet_req_complete(req, status);
337336
if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
338337
req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) {
339338
unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120;
340339

341340
mod_delayed_work(system_wq, &req->sq->auth_expired_work,
342341
auth_expire_secs * HZ);
343-
return;
342+
goto complete;
344343
}
345344
/* Final states, clear up variables */
346345
nvmet_auth_sq_free(req->sq);
347346
if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
348347
nvmet_ctrl_fatal_error(ctrl);
348+
349+
complete:
350+
nvmet_req_complete(req, status);
349351
}
350352

351353
static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
@@ -514,11 +516,12 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
514516
kfree(d);
515517
done:
516518
req->cqe->result.u64 = 0;
517-
nvmet_req_complete(req, status);
519+
518520
if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2)
519521
nvmet_auth_sq_free(req->sq);
520522
else if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
521523
nvmet_auth_sq_free(req->sq);
522524
nvmet_ctrl_fatal_error(ctrl);
523525
}
526+
nvmet_req_complete(req, status);
524527
}

0 commit comments

Comments
 (0)