aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteffen Maier <maier@linux.vnet.ibm.com>2017-10-13 09:40:07 -0400
committerMartin K. Petersen <martin.petersen@oracle.com>2017-10-16 22:45:26 -0400
commitab31fd0ce65ec93828b617123792c1bb7c6dcc42 (patch)
treee4123191263ce1da60550843d4285cba61a9faf6
parent8d30371fd7c328e192d7ea3108bd71b903631d6a (diff)
scsi: zfcp: fix erp_action use-before-initialize in REC action trace
v4.10 commit 6f2ce1c6af37 ("scsi: zfcp: fix rport unblock race with LUN recovery") extended accessing parent pointer fields of struct zfcp_erp_action for tracing. If an erp_action has never been enqueued before, these parent pointer fields are uninitialized and NULL. Examples are zfcp objects freshly added to the parent object's children list, before enqueueing their first recovery subsequently. In zfcp_erp_try_rport_unblock(), we iterate such list. Accessing erp_action fields can cause a NULL pointer dereference. Since the kernel can read from lowcore on s390, it does not immediately cause a kernel page fault. Instead it can cause hangs on trying to acquire the wrong erp_action->adapter->dbf->rec_lock in zfcp_dbf_rec_action_lvl() ^bogus^ while holding already other locks with IRQs disabled. Real life example from attaching lots of LUNs in parallel on many CPUs: crash> bt 17723 PID: 17723 TASK: ... CPU: 25 COMMAND: "zfcperp0.0.1800" LOWCORE INFO: -psw : 0x0404300180000000 0x000000000038e424 -function : _raw_spin_lock_wait_flags at 38e424 ... #0 [fdde8fc90] zfcp_dbf_rec_action_lvl at 3e0004e9862 [zfcp] #1 [fdde8fce8] zfcp_erp_try_rport_unblock at 3e0004dfddc [zfcp] #2 [fdde8fd38] zfcp_erp_strategy at 3e0004e0234 [zfcp] #3 [fdde8fda8] zfcp_erp_thread at 3e0004e0a12 [zfcp] #4 [fdde8fe60] kthread at 173550 #5 [fdde8feb8] kernel_thread_starter at 10add2 zfcp_adapter zfcp_port zfcp_unit <address>, 0x404040d600000000 scsi_device NULL, returning early! zfcp_scsi_dev.status = 0x40000000 0x40000000 ZFCP_STATUS_COMMON_RUNNING crash> zfcp_unit <address> struct zfcp_unit { erp_action = { adapter = 0x0, port = 0x0, unit = 0x0, }, } zfcp_erp_action is always fully embedded into its container object. Such container object is never moved in its object tree (only add or delete). Hence, erp_action parent pointers can never change. To fix the issue, initialize the erp_action parent pointers before adding the erp_action container to any list and thus before it becomes accessible from outside of its initializing function. In order to also close the time window between zfcp_erp_setup_act() memsetting the entire erp_action to zero and setting the parent pointers again, drop the memset and instead explicitly initialize individually all erp_action fields except for parent pointers. To be extra careful not to introduce any other unintended side effect, even keep zeroing the erp_action fields for list and timer. Also double-check with WARN_ON_ONCE that erp_action parent pointers never change, so we get to know when we would deviate from previous behavior. Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com> Fixes: 6f2ce1c6af37 ("scsi: zfcp: fix rport unblock race with LUN recovery") Cc: <stable@vger.kernel.org> #2.6.32+ Reviewed-by: Benjamin Block <bblock@linux.vnet.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/s390/scsi/zfcp_aux.c5
-rw-r--r--drivers/s390/scsi/zfcp_erp.c18
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c5
3 files changed, 21 insertions, 7 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 82ac331d9125..84752152d41f 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -357,6 +357,8 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
357 357
358 adapter->next_port_scan = jiffies; 358 adapter->next_port_scan = jiffies;
359 359
360 adapter->erp_action.adapter = adapter;
361
360 if (zfcp_qdio_setup(adapter)) 362 if (zfcp_qdio_setup(adapter))
361 goto failed; 363 goto failed;
362 364
@@ -513,6 +515,9 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
513 port->dev.groups = zfcp_port_attr_groups; 515 port->dev.groups = zfcp_port_attr_groups;
514 port->dev.release = zfcp_port_release; 516 port->dev.release = zfcp_port_release;
515 517
518 port->erp_action.adapter = adapter;
519 port->erp_action.port = port;
520
516 if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) { 521 if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) {
517 kfree(port); 522 kfree(port);
518 goto err_out; 523 goto err_out;
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 37408f5f81ce..ec2532ee1822 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -193,9 +193,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
193 atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, 193 atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE,
194 &zfcp_sdev->status); 194 &zfcp_sdev->status);
195 erp_action = &zfcp_sdev->erp_action; 195 erp_action = &zfcp_sdev->erp_action;
196 memset(erp_action, 0, sizeof(struct zfcp_erp_action)); 196 WARN_ON_ONCE(erp_action->port != port);
197 erp_action->port = port; 197 WARN_ON_ONCE(erp_action->sdev != sdev);
198 erp_action->sdev = sdev;
199 if (!(atomic_read(&zfcp_sdev->status) & 198 if (!(atomic_read(&zfcp_sdev->status) &
200 ZFCP_STATUS_COMMON_RUNNING)) 199 ZFCP_STATUS_COMMON_RUNNING))
201 act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; 200 act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
@@ -208,8 +207,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
208 zfcp_erp_action_dismiss_port(port); 207 zfcp_erp_action_dismiss_port(port);
209 atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); 208 atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
210 erp_action = &port->erp_action; 209 erp_action = &port->erp_action;
211 memset(erp_action, 0, sizeof(struct zfcp_erp_action)); 210 WARN_ON_ONCE(erp_action->port != port);
212 erp_action->port = port; 211 WARN_ON_ONCE(erp_action->sdev != NULL);
213 if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING)) 212 if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
214 act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; 213 act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
215 break; 214 break;
@@ -219,7 +218,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
219 zfcp_erp_action_dismiss_adapter(adapter); 218 zfcp_erp_action_dismiss_adapter(adapter);
220 atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); 219 atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
221 erp_action = &adapter->erp_action; 220 erp_action = &adapter->erp_action;
222 memset(erp_action, 0, sizeof(struct zfcp_erp_action)); 221 WARN_ON_ONCE(erp_action->port != NULL);
222 WARN_ON_ONCE(erp_action->sdev != NULL);
223 if (!(atomic_read(&adapter->status) & 223 if (!(atomic_read(&adapter->status) &
224 ZFCP_STATUS_COMMON_RUNNING)) 224 ZFCP_STATUS_COMMON_RUNNING))
225 act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY; 225 act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
@@ -229,7 +229,11 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
229 return NULL; 229 return NULL;
230 } 230 }
231 231
232 erp_action->adapter = adapter; 232 WARN_ON_ONCE(erp_action->adapter != adapter);
233 memset(&erp_action->list, 0, sizeof(erp_action->list));
234 memset(&erp_action->timer, 0, sizeof(erp_action->timer));
235 erp_action->step = ZFCP_ERP_STEP_UNINITIALIZED;
236 erp_action->fsf_req_id = 0;
233 erp_action->action = need; 237 erp_action->action = need;
234 erp_action->status = act_status; 238 erp_action->status = act_status;
235 239
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index ec3ddd1d31d5..6cf8732627e0 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -115,10 +115,15 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev)
115 struct zfcp_unit *unit; 115 struct zfcp_unit *unit;
116 int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE; 116 int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE;
117 117
118 zfcp_sdev->erp_action.adapter = adapter;
119 zfcp_sdev->erp_action.sdev = sdev;
120
118 port = zfcp_get_port_by_wwpn(adapter, rport->port_name); 121 port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
119 if (!port) 122 if (!port)
120 return -ENXIO; 123 return -ENXIO;
121 124
125 zfcp_sdev->erp_action.port = port;
126
122 unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev)); 127 unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev));
123 if (unit) 128 if (unit)
124 put_device(&unit->dev); 129 put_device(&unit->dev);