[PATCH] megaraid_sas: fix NULL pointer dereference in case of command via ioctl timeout

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

A NULL pointer dereference occurs if a command via ioctl times
out.

When a command is submitted via ioctl to megaraid_sas firmware,
the processing goes through like this:

  megasas_mgmt_ioctl()
    megasas_mgmt_ioctl_fw()
      megasas_mgmt_fw_ioctl()
        cmd->sync_cmd = 1
        megasas_issue_blocked_cmd()
          instance->instancet->fire_cmd() // command submitted
          wait_event_timeout(instance->int_cmd_wait_q,
                             cmd->cmd_status != ENODATA, ...)<--+
                                                                |
Normally, the firmware sends back a reply to the driver and it  |
wakes up the waiting processing (above) like this:              |
                                                                |
  interrupt                                                     |
    |                                                           |
    V                                                           |
  megasas_isr()                                                 |
    megasas_deplete_reply_queue()                               |
      tasklet_schedule(&instance->isr_tasklet)                  |
        |                                                       |
        V                                                       |
      megasas_complete_cmd_dpc()                                |
        megasas_complete_cmd()                                  |
          case MFI_CMD_PD_SCSI_IO:                              |
            if (cmd->sync_cmd)                                  |
              megasas_complete_int_cmd()                        |
                cmd->cmd_status = cmd->frame->io.cmd_status     |
                wake_up(&instance->int_cmd_wait_q) -------------+

If the command times out for some reason, however, it wakes up
and restarts the processing forward like this:

  wakes up and returns from megasas_issue_blocked_cmd()
  cmd->sync_cmd = 0
  ...
  megasas_return_cmd()

After that, if the command is executed and the firmware sends a
reply to the driver, since cmd->sync_cmd has been set to 0, the
processing goes like below (READ/WRITE command handling) and the
NULL pointer deference occurs, causing an Oops.

  interrupt
    |
    V
  megasas_isr()
    megasas_deplete_reply_queue()
      tasklet_schedule(&instance->isr_tasklet)
        |
        V
      megasas_complete_cmd_dpc()
        megasas_complete_cmd()
          case MFI_CMD_PD_SCSI_IO:
            // does nothing and falls through
          case MFI_CMD_LD_READ/WRITE:
            dereference to cmd->scmd that is NULL

I think something like the following patch should fix the issue.

Thanks,
Kei


Signed-off-by: Kei Tokunaga <tokunaga.keiich@xxxxxxxxxxxxxx>
---
 drivers/scsi/megaraid/megaraid_sas.c |   22 ++++++++++++++++++++--
 1 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c
index 99e4478..4127790 100644
--- a/drivers/scsi/megaraid/megaraid_sas.c
+++ b/drivers/scsi/megaraid/megaraid_sas.c
@@ -594,13 +594,21 @@ static int
 megasas_issue_blocked_cmd(struct megasas_instance *instance,
 			  struct megasas_cmd *cmd)
 {
+	long timeleft;
+
 	cmd->cmd_status = ENODATA;
 
 	instance->instancet->fire_cmd(instance,
 			cmd->frame_phys_addr, 0, instance->reg_set);
 
-	wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
-		MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
+	timeleft = wait_event_timeout(instance->int_cmd_wait_q,
+				      (cmd->cmd_status != ENODATA),
+				      MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
+
+	if (!timeleft) {
+		printk(KERN_ERR "megasas: command timed out\n");
+		cmd->cmd_status = ETIME;
+	}
 
 	return 0;
 }
@@ -1671,6 +1679,10 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
 	case MFI_CMD_PD_SCSI_IO:
 	case MFI_CMD_LD_SCSI_IO:
 
+		/* command timed out and there is nothing to do. */
+		if (unlikely(cmd->cmd_status == ETIME))
+			break;
+
 		/*
 		 * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been
 		 * issued either through an IO path or an IOCTL path. If it
@@ -3718,6 +3730,12 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
 	megasas_issue_blocked_cmd(instance, cmd);
 	cmd->sync_cmd = 0;
 
+	/* command timed out */
+	if (cmd->cmd_status == ETIME) {
+		error = -ETIME;
+		goto out;
+	}
+
 	/*
 	 * copy out the kernel buffers to user buffers
 	 */



--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux