Commit d436de8c authored by Martin Peschke's avatar Martin Peschke Committed by James Bottomley

[SCSI] zfcp: only access zfcp_scsi_dev for valid scsi_device

__scsi_remove_device (e.g. due to dev_loss_tmo) calls
zfcp_scsi_slave_destroy which in turn sends a close LUN FSF request to
the adapter. After 30 seconds without response,
zfcp_erp_timeout_handler kicks the ERP thread failing the close LUN
ERP action. zfcp_erp_wait in zfcp_erp_lun_shutdown_wait and thus
zfcp_scsi_slave_destroy returns and then scsi_device is no longer
valid. Sometime later the response to the close LUN FSF request may
finally come in. However, commit
b62a8d9b
"[SCSI] zfcp: Use SCSI device data zfcp_scsi_dev instead of zfcp_unit"
introduced a number of attempts to unconditionally access struct
zfcp_scsi_dev through struct scsi_device causing a use-after-free.
This leads to an Oops due to kernel page fault in one of:
zfcp_fsf_abort_fcp_command_handler, zfcp_fsf_open_lun_handler,
zfcp_fsf_close_lun_handler, zfcp_fsf_req_trace,
zfcp_fsf_fcp_handler_common.
Move dereferencing of zfcp private data zfcp_scsi_dev allocated in
scsi_device via scsi_transport_reserve_device after the check for
potentially aborted FSF request and thus no longer valid scsi_device.
Only then assign sdev_to_zfcp(sdev) to the local auto variable struct
zfcp_scsi_dev *zfcp_sdev.
Signed-off-by: default avatarMartin Peschke <mpeschke@linux.vnet.ibm.com>
Signed-off-by: default avatarSteffen Maier <maier@linux.vnet.ibm.com>
Cc: <stable@vger.kernel.org> #2.6.37+
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 43f60cbd
...@@ -801,12 +801,14 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio) ...@@ -801,12 +801,14 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req) static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
{ {
struct scsi_device *sdev = req->data; struct scsi_device *sdev = req->data;
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_scsi_dev *zfcp_sdev;
union fsf_status_qual *fsq = &req->qtcb->header.fsf_status_qual; union fsf_status_qual *fsq = &req->qtcb->header.fsf_status_qual;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR) if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
return; return;
zfcp_sdev = sdev_to_zfcp(sdev);
switch (req->qtcb->header.fsf_status) { switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID: case FSF_PORT_HANDLE_NOT_VALID:
if (fsq->word[0] == fsq->word[1]) { if (fsq->word[0] == fsq->word[1]) {
...@@ -1769,13 +1771,15 @@ static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *req) ...@@ -1769,13 +1771,15 @@ static void zfcp_fsf_open_lun_handler(struct zfcp_fsf_req *req)
{ {
struct zfcp_adapter *adapter = req->adapter; struct zfcp_adapter *adapter = req->adapter;
struct scsi_device *sdev = req->data; struct scsi_device *sdev = req->data;
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_scsi_dev *zfcp_sdev;
struct fsf_qtcb_header *header = &req->qtcb->header; struct fsf_qtcb_header *header = &req->qtcb->header;
struct fsf_qtcb_bottom_support *bottom = &req->qtcb->bottom.support; struct fsf_qtcb_bottom_support *bottom = &req->qtcb->bottom.support;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR) if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
return; return;
zfcp_sdev = sdev_to_zfcp(sdev);
atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
ZFCP_STATUS_COMMON_ACCESS_BOXED | ZFCP_STATUS_COMMON_ACCESS_BOXED |
ZFCP_STATUS_LUN_SHARED | ZFCP_STATUS_LUN_SHARED |
...@@ -1886,11 +1890,13 @@ int zfcp_fsf_open_lun(struct zfcp_erp_action *erp_action) ...@@ -1886,11 +1890,13 @@ int zfcp_fsf_open_lun(struct zfcp_erp_action *erp_action)
static void zfcp_fsf_close_lun_handler(struct zfcp_fsf_req *req) static void zfcp_fsf_close_lun_handler(struct zfcp_fsf_req *req)
{ {
struct scsi_device *sdev = req->data; struct scsi_device *sdev = req->data;
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_scsi_dev *zfcp_sdev;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR) if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
return; return;
zfcp_sdev = sdev_to_zfcp(sdev);
switch (req->qtcb->header.fsf_status) { switch (req->qtcb->header.fsf_status) {
case FSF_PORT_HANDLE_NOT_VALID: case FSF_PORT_HANDLE_NOT_VALID:
zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fscuh_1"); zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fscuh_1");
...@@ -1980,7 +1986,7 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi) ...@@ -1980,7 +1986,7 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi)
{ {
struct fsf_qual_latency_info *lat_in; struct fsf_qual_latency_info *lat_in;
struct latency_cont *lat = NULL; struct latency_cont *lat = NULL;
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scsi->device); struct zfcp_scsi_dev *zfcp_sdev;
struct zfcp_blk_drv_data blktrc; struct zfcp_blk_drv_data blktrc;
int ticks = req->adapter->timer_ticks; int ticks = req->adapter->timer_ticks;
...@@ -1995,6 +2001,7 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi) ...@@ -1995,6 +2001,7 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi)
if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA && if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA &&
!(req->status & ZFCP_STATUS_FSFREQ_ERROR)) { !(req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
zfcp_sdev = sdev_to_zfcp(scsi->device);
blktrc.flags |= ZFCP_BLK_LAT_VALID; blktrc.flags |= ZFCP_BLK_LAT_VALID;
blktrc.channel_lat = lat_in->channel_lat * ticks; blktrc.channel_lat = lat_in->channel_lat * ticks;
blktrc.fabric_lat = lat_in->fabric_lat * ticks; blktrc.fabric_lat = lat_in->fabric_lat * ticks;
...@@ -2032,12 +2039,14 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) ...@@ -2032,12 +2039,14 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req)
{ {
struct scsi_cmnd *scmnd = req->data; struct scsi_cmnd *scmnd = req->data;
struct scsi_device *sdev = scmnd->device; struct scsi_device *sdev = scmnd->device;
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_scsi_dev *zfcp_sdev;
struct fsf_qtcb_header *header = &req->qtcb->header; struct fsf_qtcb_header *header = &req->qtcb->header;
if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR))
return; return;
zfcp_sdev = sdev_to_zfcp(sdev);
switch (header->fsf_status) { switch (header->fsf_status) {
case FSF_HANDLE_MISMATCH: case FSF_HANDLE_MISMATCH:
case FSF_PORT_HANDLE_NOT_VALID: case FSF_PORT_HANDLE_NOT_VALID:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment