Skip to content

Commit 14936b1

Browse files
mikechristiemartinkpetersen
authored andcommitted
scsi: libiscsi: Fix iscsi_task use after free()
The following bug was reported and debugged by wubo40@huawei.com: When testing kernel 4.18 version, NULL pointer dereference problem occurs in iscsi_eh_cmd_timed_out() function. I think this bug in the upstream is still exists. The analysis reasons are as follows: 1) For some reason, I/O command did not complete within the timeout period. The block layer timer works, call scsi_times_out() to handle I/O timeout logic. At the same time the command just completes. 2) scsi_times_out() call iscsi_eh_cmd_timed_out() to process timeout logic. Although there is an NULL judgment for the task, the task has not been released yet now. 3) iscsi_complete_task() calls __iscsi_put_task(). The task reference count reaches zero, the conditions for free task is met, then iscsi_free_task() frees the task, and sets sc->SCp.ptr = NULL. After iscsi_eh_cmd_timed_out() passes the task judgment check, there can still be NULL dereference scenarios. CPU0 CPU3 |- scsi_times_out() |- iscsi_complete_task() | | |- iscsi_eh_cmd_timed_out() |- __iscsi_put_task() | | |- task=sc->SCp.ptr, task is not NUL, check passed |- iscsi_free_task(task) | | | |-> sc->SCp.ptr = NULL | | |- task is NULL now, NULL pointer dereference | | | \|/ \|/ Calltrace: [380751.840862] BUG: unable to handle kernel NULL pointer dereference at 0000000000000138 [380751.843709] PGD 0 P4D 0 [380751.844770] Oops: 0000 [#1] SMP PTI [380751.846283] CPU: 0 PID: 403 Comm: kworker/0:1H Kdump: loaded Tainted: G [380751.851467] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996) [380751.856521] Workqueue: kblockd blk_mq_timeout_work [380751.858527] RIP: 0010:iscsi_eh_cmd_timed_out+0x15e/0x2e0 [libiscsi] [380751.861129] Code: 83 ea 01 48 8d 74 d0 08 48 8b 10 48 8b 4a 50 48 85 c9 74 2c 48 39 d5 74 [380751.868811] RSP: 0018:ffffc1e280a5fd58 EFLAGS: 00010246 [380751.870978] RAX: ffff9fd1e84e15e0 RBX: ffff9fd1e84e6dd0 RCX: 0000000116acc580 [380751.873791] RDX: ffff9fd1f97a9400 RSI: ffff9fd1e84e1800 RDI: ffff9fd1e4d6d420 [380751.876059] RBP: ffff9fd1e4d49000 R08: 0000000116acc580 R09: 0000000116acc580 [380751.878284] R10: 0000000000000000 R11: 0000000000000000 R12: ffff9fd1e6e931e8 [380751.880500] R13: ffff9fd1e84e6ee0 R14: 0000000000000010 R15: 0000000000000003 [380751.882687] FS: 0000000000000000(0000) GS:ffff9fd1fac00000(0000) knlGS:0000000000000000 [380751.885236] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [380751.887059] CR2: 0000000000000138 CR3: 000000011860a001 CR4: 00000000003606f0 [380751.889308] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [380751.891523] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [380751.893738] Call Trace: [380751.894639] scsi_times_out+0x60/0x1c0 [380751.895861] blk_mq_check_expired+0x144/0x200 [380751.897302] ? __switch_to_asm+0x35/0x70 [380751.898551] blk_mq_queue_tag_busy_iter+0x195/0x2e0 [380751.900091] ? __blk_mq_requeue_request+0x100/0x100 [380751.901611] ? __switch_to_asm+0x41/0x70 [380751.902853] ? __blk_mq_requeue_request+0x100/0x100 [380751.904398] blk_mq_timeout_work+0x54/0x130 [380751.905740] process_one_work+0x195/0x390 [380751.907228] worker_thread+0x30/0x390 [380751.908713] ? process_one_work+0x390/0x390 [380751.910350] kthread+0x10d/0x130 [380751.911470] ? kthread_flush_work_fn+0x10/0x10 [380751.913007] ret_from_fork+0x35/0x40 crash> dis -l iscsi_eh_cmd_timed_out+0x15e xxxxx/drivers/scsi/libiscsi.c: 2062 1970 enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) { ... 1984 spin_lock_bh(&session->frwd_lock); 1985 task = (struct iscsi_task *)sc->SCp.ptr; 1986 if (!task) { 1987 /* 1988 * Raced with completion. Blk layer has taken ownership 1989 * so let timeout code complete it now. 1990 */ 1991 rc = BLK_EH_DONE; 1992 goto done; 1993 } ... 2052 for (i = 0; i < conn->session->cmds_max; i++) { 2053 running_task = conn->session->cmds[i]; 2054 if (!running_task->sc || running_task == task || 2055 running_task->state != ISCSI_TASK_RUNNING) 2056 continue; 2057 2058 /* 2059 * Only check if cmds started before this one have made 2060 * progress, or this could never fail 2061 */ 2062 if (time_after(running_task->sc->jiffies_at_alloc, 2063 task->sc->jiffies_at_alloc)) <--- 2064 continue; 2065 ... } carsh> struct scsi_cmnd ffff9fd1e6e931e8 struct scsi_cmnd { ... SCp = { ptr = 0x0, <--- iscsi_task this_residual = 0, ... }, } To prevent this, we take a ref to the cmd under the back (completion) lock so if the completion side were to call iscsi_complete_task() on the task while the timer/eh paths are not holding the back_lock it will not be freed from under us. Note that this requires the previous patch, "scsi: libiscsi: Drop taskqueuelock" because bnx2i sleeps in its cleanup_task callout if the cmd is aborted. If the EH/timer and completion path are racing we don't know which path will do the last put. The previous patch moved the operations we needed to do under the forward lock to cleanup_queued_task. Once that has run we can drop the forward lock for the cmd and bnx2i no longer has to worry about if the EH, timer or completion path did the ast put and if the forward lock is held or not since it won't be. Link: https://lore.kernel.org/r/20210207044608.27585-4-michael.christie@oracle.com Reported-by: Wu Bo <wubo40@huawei.com> Reviewed-by: Lee Duncan <lduncan@suse.com> Signed-off-by: Mike Christie <michael.christie@oracle.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent 5923d64 commit 14936b1

File tree

2 files changed

+45
-28
lines changed

2 files changed

+45
-28
lines changed

drivers/scsi/bnx2i/bnx2i_iscsi.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,10 +1171,8 @@ static void bnx2i_cleanup_task(struct iscsi_task *task)
11711171
bnx2i_send_cmd_cleanup_req(hba, task->dd_data);
11721172

11731173
spin_unlock_bh(&conn->session->back_lock);
1174-
spin_unlock_bh(&conn->session->frwd_lock);
11751174
wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
11761175
msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
1177-
spin_lock_bh(&conn->session->frwd_lock);
11781176
spin_lock_bh(&conn->session->back_lock);
11791177
}
11801178
bnx2i_iscsi_unmap_sg_list(task->dd_data);

drivers/scsi/libiscsi.c

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -587,26 +587,15 @@ static bool cleanup_queued_task(struct iscsi_task *task)
587587
}
588588

589589
/*
590-
* session frwd_lock must be held and if not called for a task that is
591-
* still pending or from the xmit thread, then xmit thread must
592-
* be suspended.
590+
* session frwd lock must be held and if not called for a task that is still
591+
* pending or from the xmit thread, then xmit thread must be suspended
593592
*/
594593
static void fail_scsi_task(struct iscsi_task *task, int err)
595594
{
596595
struct iscsi_conn *conn = task->conn;
597596
struct scsi_cmnd *sc;
598597
int state;
599598

600-
/*
601-
* if a command completes and we get a successful tmf response
602-
* we will hit this because the scsi eh abort code does not take
603-
* a ref to the task.
604-
*/
605-
sc = task->sc;
606-
if (!sc)
607-
return;
608-
609-
/* regular RX path uses back_lock */
610599
spin_lock_bh(&conn->session->back_lock);
611600
if (cleanup_queued_task(task)) {
612601
spin_unlock_bh(&conn->session->back_lock);
@@ -626,6 +615,7 @@ static void fail_scsi_task(struct iscsi_task *task, int err)
626615
else
627616
state = ISCSI_TASK_ABRT_TMF;
628617

618+
sc = task->sc;
629619
sc->result = err << 16;
630620
scsi_set_resid(sc, scsi_bufflen(sc));
631621
iscsi_complete_task(task, state);
@@ -1893,27 +1883,39 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
18931883
}
18941884

18951885
/*
1896-
* Fail commands. session lock held and recv side suspended and xmit
1897-
* thread flushed
1886+
* Fail commands. session frwd lock held and xmit thread flushed.
18981887
*/
18991888
static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error)
19001889
{
1890+
struct iscsi_session *session = conn->session;
19011891
struct iscsi_task *task;
19021892
int i;
19031893

1904-
for (i = 0; i < conn->session->cmds_max; i++) {
1905-
task = conn->session->cmds[i];
1894+
spin_lock_bh(&session->back_lock);
1895+
for (i = 0; i < session->cmds_max; i++) {
1896+
task = session->cmds[i];
19061897
if (!task->sc || task->state == ISCSI_TASK_FREE)
19071898
continue;
19081899

19091900
if (lun != -1 && lun != task->sc->device->lun)
19101901
continue;
19111902

1912-
ISCSI_DBG_SESSION(conn->session,
1903+
__iscsi_get_task(task);
1904+
spin_unlock_bh(&session->back_lock);
1905+
1906+
ISCSI_DBG_SESSION(session,
19131907
"failing sc %p itt 0x%x state %d\n",
19141908
task->sc, task->itt, task->state);
19151909
fail_scsi_task(task, error);
1910+
1911+
spin_unlock_bh(&session->frwd_lock);
1912+
iscsi_put_task(task);
1913+
spin_lock_bh(&session->frwd_lock);
1914+
1915+
spin_lock_bh(&session->back_lock);
19161916
}
1917+
1918+
spin_unlock_bh(&session->back_lock);
19171919
}
19181920

19191921
/**
@@ -1991,15 +1993,19 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
19911993
ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc);
19921994

19931995
spin_lock_bh(&session->frwd_lock);
1996+
spin_lock(&session->back_lock);
19941997
task = (struct iscsi_task *)sc->SCp.ptr;
19951998
if (!task) {
19961999
/*
19972000
* Raced with completion. Blk layer has taken ownership
19982001
* so let timeout code complete it now.
19992002
*/
20002003
rc = BLK_EH_DONE;
2004+
spin_unlock(&session->back_lock);
20012005
goto done;
20022006
}
2007+
__iscsi_get_task(task);
2008+
spin_unlock(&session->back_lock);
20032009

20042010
if (session->state != ISCSI_STATE_LOGGED_IN) {
20052011
/*
@@ -2058,6 +2064,7 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
20582064
goto done;
20592065
}
20602066

2067+
spin_lock(&session->back_lock);
20612068
for (i = 0; i < conn->session->cmds_max; i++) {
20622069
running_task = conn->session->cmds[i];
20632070
if (!running_task->sc || running_task == task ||
@@ -2090,10 +2097,12 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
20902097
"last xfer %lu/%lu. Last check %lu.\n",
20912098
task->last_xfer, running_task->last_xfer,
20922099
task->last_timeout);
2100+
spin_unlock(&session->back_lock);
20932101
rc = BLK_EH_RESET_TIMER;
20942102
goto done;
20952103
}
20962104
}
2105+
spin_unlock(&session->back_lock);
20972106

20982107
/* Assumes nop timeout is shorter than scsi cmd timeout */
20992108
if (task->have_checked_conn)
@@ -2115,9 +2124,12 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
21152124
rc = BLK_EH_RESET_TIMER;
21162125

21172126
done:
2118-
if (task)
2119-
task->last_timeout = jiffies;
21202127
spin_unlock_bh(&session->frwd_lock);
2128+
2129+
if (task) {
2130+
task->last_timeout = jiffies;
2131+
iscsi_put_task(task);
2132+
}
21212133
ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ?
21222134
"timer reset" : "shutdown or nh");
21232135
return rc;
@@ -2225,15 +2237,20 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
22252237
conn->eh_abort_cnt++;
22262238
age = session->age;
22272239

2240+
spin_lock(&session->back_lock);
22282241
task = (struct iscsi_task *)sc->SCp.ptr;
2229-
ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n",
2230-
sc, task->itt);
2231-
2232-
/* task completed before time out */
2233-
if (!task->sc) {
2242+
if (!task || !task->sc) {
2243+
/* task completed before time out */
22342244
ISCSI_DBG_EH(session, "sc completed while abort in progress\n");
2235-
goto success;
2245+
2246+
spin_unlock(&session->back_lock);
2247+
spin_unlock_bh(&session->frwd_lock);
2248+
mutex_unlock(&session->eh_mutex);
2249+
return SUCCESS;
22362250
}
2251+
ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt);
2252+
__iscsi_get_task(task);
2253+
spin_unlock(&session->back_lock);
22372254

22382255
if (task->state == ISCSI_TASK_PENDING) {
22392256
fail_scsi_task(task, DID_ABORT);
@@ -2295,6 +2312,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
22952312
success_unlocked:
22962313
ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n",
22972314
sc, task->itt);
2315+
iscsi_put_task(task);
22982316
mutex_unlock(&session->eh_mutex);
22992317
return SUCCESS;
23002318

@@ -2303,6 +2321,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
23032321
failed_unlocked:
23042322
ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc,
23052323
task ? task->itt : 0);
2324+
iscsi_put_task(task);
23062325
mutex_unlock(&session->eh_mutex);
23072326
return FAILED;
23082327
}

0 commit comments

Comments
 (0)