Skip to content

Commit a86a758

Browse files
dmitry-fomichevmartinkpetersen
authored andcommitted
scsi: target: tcmu: avoid use-after-free after command timeout
In tcmu_handle_completion() function, the variable called read_len is always initialized with a value taken from se_cmd structure. If this function is called to complete an expired (timed out) out command, the session command pointed by se_cmd is likely to be already deallocated by the target core at that moment. As the result, this access triggers a use-after-free warning from KASAN. This patch fixes the code not to touch se_cmd when completing timed out TCMU commands. It also resets the pointer to se_cmd at the time when the TCMU_CMD_BIT_EXPIRED flag is set because it is going to become invalid after calling target_complete_cmd() later in the same function, tcmu_check_expired_cmd(). Signed-off-by: Dmitry Fomichev <[email protected]> Acked-by: Mike Christie <[email protected]> Reviewed-by: Damien Le Moal <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 26fa656 commit a86a758

File tree

1 file changed

+7
-2
lines changed

1 file changed

+7
-2
lines changed

drivers/target/target_core_user.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,14 +1132,16 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
11321132
struct se_cmd *se_cmd = cmd->se_cmd;
11331133
struct tcmu_dev *udev = cmd->tcmu_dev;
11341134
bool read_len_valid = false;
1135-
uint32_t read_len = se_cmd->data_length;
1135+
uint32_t read_len;
11361136

11371137
/*
11381138
* cmd has been completed already from timeout, just reclaim
11391139
* data area space and free cmd
11401140
*/
1141-
if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags))
1141+
if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) {
1142+
WARN_ON_ONCE(se_cmd);
11421143
goto out;
1144+
}
11431145

11441146
list_del_init(&cmd->queue_entry);
11451147

@@ -1152,6 +1154,7 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
11521154
goto done;
11531155
}
11541156

1157+
read_len = se_cmd->data_length;
11551158
if (se_cmd->data_direction == DMA_FROM_DEVICE &&
11561159
(entry->hdr.uflags & TCMU_UFLAG_READ_LEN) && entry->rsp.read_len) {
11571160
read_len_valid = true;
@@ -1307,6 +1310,7 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data)
13071310
*/
13081311
scsi_status = SAM_STAT_CHECK_CONDITION;
13091312
list_del_init(&cmd->queue_entry);
1313+
cmd->se_cmd = NULL;
13101314
} else {
13111315
list_del_init(&cmd->queue_entry);
13121316
idr_remove(&udev->commands, id);
@@ -2022,6 +2026,7 @@ static void tcmu_reset_ring(struct tcmu_dev *udev, u8 err_level)
20222026

20232027
idr_remove(&udev->commands, i);
20242028
if (!test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) {
2029+
WARN_ON(!cmd->se_cmd);
20252030
list_del_init(&cmd->queue_entry);
20262031
if (err_level == 1) {
20272032
/*

0 commit comments

Comments
 (0)