megaraid_sas: fix NULL pointer dereference in case of command via ioctl timeout
Subsystems (2)
PATCH 1/1 megaraid_sas: fix NULL pointer dereference in case of command via ioctl timeout
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 NULLI think something like the following patch should fix the issue.
Thanks,
Kei
--- drivers/scsi/megaraid/megaraid_sas.c | 22 ++++++++++++++++++++-- 1 files changed, 20 insertions(+), 2 deletions(-)
drivers/scsi/megaraid/megaraid_sas.c +20 -2
@@ -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 */