[ofa-general] Re: sg_reset can trigger a NULL pointer dereference in the SRP initiator
Bart Van Assche
bart.vanassche at gmail.com
Thu Aug 6 00:39:45 PDT 2009
On Wed, Aug 5, 2009 at 10:37 PM, James
Bottomley<James.Bottomley at hansenpartnership.com> wrote:
> On Wed, 2009-08-05 at 19:54 +0200, Bart Van Assche wrote:
>> On Wed, Aug 5, 2009 at 7:44 PM, Roland Dreier<rdreier at cisco.com> wrote:
>> >
>> > > The NULL pointer dereference happens when srp_reset_device() calls
>> > > srp_send_tsk_mgmt(target, req, SRP_TSK_LUN_RESET) with
>> > > req->scmnd->device == NULL. When the sg_reset command issues an
>> > > SG_SCSI_RESET ioctl, scsi_reset_provider() is invoked and allocates an
>> > > scmnd structure and sets scmnd->device to NULL. It is this scmnd
>> > > structure that is passed to srp_reset_device(). What I'm not sure
>> > > about is whether scsi_reset_provider() should set req->scmnd->device
>> > > to a non-NULL value or whether srp_send_tsk_mgmt() should be able to
>> > > handle the condition req->scmnd->device == NULL.
>> >
>> > Well, I don't see how the reset ioctl can do anything useful unless it
>> > passes a device in with the scsi command -- otherwise for example
>> > srp_reset_device() has no idea what LUN to try and reset.
>>
>> (added linux-scsi in CC)
>>
>> I hope one of the SCSI people can tell us whether the behavior that
>> scsi_reset_provider()
>> passes the value NULL in req->scmnd->device to
>> scsi_try_bus_device_reset() is correct ?
>
> Need more information.
>
> cmd->device is supposed to be initialised in scsi_get_command(), which
> scsi_reset_provider() calls ... why do you think it got set to null?
This thread started with the observation that it is easy to trigger a
NULL pointer dereference in the SRP initiator
(http://bugzilla.kernel.org/show_bug.cgi?id=13893). The following
sequence is sufficient:
* Remove the ib_srp kernel module (doing so closes all active SRP sessions).
* Insert the ib_srp kernel module.
* Create a new SRP connection.
* Issue the sg_reset -d ${srp_device} command in a shell.
The sg_reset command issues an SG_SCSI_RESET ioctl. This ioctl is
processed by invoking scsi_reset_provider(), which in turns invokes
the eh_device_reset_handler method of the SRP initiator. Further
analysis showed that scsi_reset_provider() passes a non-NULL
cmd->device pointer to the SRP initiator, but that the SRP initiator
does not use this value. Instead srp_find_req() looks up a struct
srp_request pointer based on the struct scsi_cmnd * argument and
continues with the struct scsi_cmnd pointer contained in the struct
srp_request.
While I'm not sure that the patch below makes any sense, it makes the
NULL pointer dereference disappear. This made me wonder which
assumptions srp_find_req() is based on ?
--- linux-2.6.30.4/drivers/infiniband/ulp/srp/ib_srp-orig.c
2009-08-03 12:13:11.000000000 +0200
+++ linux-2.6.30.4/drivers/infiniband/ulp/srp/ib_srp.c 2009-08-06
08:50:30.000000000 +0200
@@ -1325,16 +1325,19 @@ static int srp_cm_handler(struct ib_cm_i
}
static int srp_send_tsk_mgmt(struct srp_target_port *target,
+ struct scsi_cmnd *scmnd,
struct srp_request *req, u8 func)
{
struct srp_iu *iu;
struct srp_tsk_mgmt *tsk_mgmt;
+ BUG_ON(!scmnd->device);
+
spin_lock_irq(target->scsi_host->host_lock);
if (target->state == SRP_TARGET_DEAD ||
target->state == SRP_TARGET_REMOVED) {
- req->scmnd->result = DID_BAD_TARGET << 16;
+ scmnd->result = DID_BAD_TARGET << 16;
goto out;
}
@@ -1348,7 +1351,7 @@ static int srp_send_tsk_mgmt(struct srp_
memset(tsk_mgmt, 0, sizeof *tsk_mgmt);
tsk_mgmt->opcode = SRP_TSK_MGMT;
- tsk_mgmt->lun = cpu_to_be64((u64)
req->scmnd->device->lun << 48);
+ tsk_mgmt->lun = cpu_to_be64((u64) scmnd->device->lun << 48);
tsk_mgmt->tag = req->index | SRP_TAG_TSK_MGMT;
tsk_mgmt->tsk_mgmt_func = func;
tsk_mgmt->task_tag = req->index;
@@ -1395,7 +1398,7 @@ static int srp_abort(struct scsi_cmnd *s
return FAILED;
if (srp_find_req(target, scmnd, &req))
return FAILED;
- if (srp_send_tsk_mgmt(target, req, SRP_TSK_ABORT_TASK))
+ if (srp_send_tsk_mgmt(target, scmnd, req, SRP_TSK_ABORT_TASK))
return FAILED;
spin_lock_irq(target->scsi_host->host_lock);
@@ -1425,7 +1428,9 @@ static int srp_reset_device(struct scsi_
return FAILED;
if (srp_find_req(target, scmnd, &req))
return FAILED;
- if (srp_send_tsk_mgmt(target, req, SRP_TSK_LUN_RESET))
+ if (WARN_ON(!scmnd->device))
+ return FAILED;
+ if (srp_send_tsk_mgmt(target, scmnd, req, SRP_TSK_LUN_RESET))
return FAILED;
if (req->tsk_status)
return FAILED;
Bart.
More information about the general
mailing list