Skip to content

Commit 057085e

Browse files
author
Nicholas Bellinger
committed
target: Fix race for SCF_COMPARE_AND_WRITE_POST checking
This patch addresses a race + use after free where the first stage of COMPARE_AND_WRITE in compare_and_write_callback() is rescheduled after the backend sends the secondary WRITE, resulting in second stage compare_and_write_post() callback completing in target_complete_ok_work() before the first can return. Because current code depends on checking se_cmd->se_cmd_flags after return from se_cmd->transport_complete_callback(), this results in first stage having SCF_COMPARE_AND_WRITE_POST set, which incorrectly falls through into second stage CAW processing code, eventually triggering a NULL pointer dereference due to use after free. To address this bug, pass in a new *post_ret parameter into se_cmd->transport_complete_callback(), and depend upon this value instead of ->se_cmd_flags to determine when to return or fall through into ->queue_status() code for CAW. Cc: Sagi Grimberg <sagig@mellanox.com> Cc: <stable@vger.kernel.org> # v3.12+ Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
1 parent ca82c2b commit 057085e

File tree

3 files changed

+18
-11
lines changed

3 files changed

+18
-11
lines changed

drivers/target/target_core_sbc.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,8 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
371371
return 0;
372372
}
373373

374-
static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success)
374+
static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success,
375+
int *post_ret)
375376
{
376377
unsigned char *buf, *addr;
377378
struct scatterlist *sg;
@@ -437,7 +438,8 @@ sbc_execute_rw(struct se_cmd *cmd)
437438
cmd->data_direction);
438439
}
439440

440-
static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
441+
static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
442+
int *post_ret)
441443
{
442444
struct se_device *dev = cmd->se_dev;
443445

@@ -447,8 +449,10 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
447449
* sent to the backend driver.
448450
*/
449451
spin_lock_irq(&cmd->t_state_lock);
450-
if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status)
452+
if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) {
451453
cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
454+
*post_ret = 1;
455+
}
452456
spin_unlock_irq(&cmd->t_state_lock);
453457

454458
/*
@@ -460,7 +464,8 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
460464
return TCM_NO_SENSE;
461465
}
462466

463-
static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success)
467+
static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
468+
int *post_ret)
464469
{
465470
struct se_device *dev = cmd->se_dev;
466471
struct scatterlist *write_sg = NULL, *sg;

drivers/target/target_core_transport.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,7 +1658,7 @@ bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags)
16581658
void transport_generic_request_failure(struct se_cmd *cmd,
16591659
sense_reason_t sense_reason)
16601660
{
1661-
int ret = 0;
1661+
int ret = 0, post_ret = 0;
16621662

16631663
pr_debug("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08llx"
16641664
" CDB: 0x%02x\n", cmd, cmd->tag, cmd->t_task_cdb[0]);
@@ -1680,7 +1680,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
16801680
*/
16811681
if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
16821682
cmd->transport_complete_callback)
1683-
cmd->transport_complete_callback(cmd, false);
1683+
cmd->transport_complete_callback(cmd, false, &post_ret);
16841684

16851685
switch (sense_reason) {
16861686
case TCM_NON_EXISTENT_LUN:
@@ -2068,11 +2068,13 @@ static void target_complete_ok_work(struct work_struct *work)
20682068
*/
20692069
if (cmd->transport_complete_callback) {
20702070
sense_reason_t rc;
2071+
bool caw = (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE);
2072+
bool zero_dl = !(cmd->data_length);
2073+
int post_ret = 0;
20712074

2072-
rc = cmd->transport_complete_callback(cmd, true);
2073-
if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
2074-
if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
2075-
!cmd->data_length)
2075+
rc = cmd->transport_complete_callback(cmd, true, &post_ret);
2076+
if (!rc && !post_ret) {
2077+
if (caw && zero_dl)
20762078
goto queue_rsp;
20772079

20782080
return;

include/target/target_core_base.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ struct se_cmd {
474474
struct completion cmd_wait_comp;
475475
const struct target_core_fabric_ops *se_tfo;
476476
sense_reason_t (*execute_cmd)(struct se_cmd *);
477-
sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool);
477+
sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool, int *);
478478
void *protocol_data;
479479

480480
unsigned char *t_task_cdb;

0 commit comments

Comments
 (0)