Skip to content

Commit c17f3b1

Browse files
steffen-maierDennySPB
authored andcommitted
scsi: zfcp: fix erp_action use-before-initialize in REC action trace
commit ab31fd0ce65ec93828b617123792c1bb7c6dcc42 upstream. 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] AOSP-JF-MM#2 [fdde8fd38] zfcp_erp_strategy at 3e0004e0234 [zfcp] CyanogenMod#3 [fdde8fda8] zfcp_erp_thread at 3e0004e0a12 [zfcp] CyanogenMod#4 [fdde8fe60] kthread at 173550 CyanogenMod#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") Reviewed-by: Benjamin Block <bblock@linux.vnet.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0b38023 commit c17f3b1

3 files changed

Lines changed: 21 additions & 7 deletions

File tree

drivers/s390/scsi/zfcp_aux.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
356356
INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
357357
INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update);
358358

359+
adapter->erp_action.adapter = adapter;
360+
359361
if (zfcp_qdio_setup(adapter))
360362
goto failed;
361363

@@ -512,6 +514,9 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
512514
port->dev.groups = zfcp_port_attr_groups;
513515
port->dev.release = zfcp_port_release;
514516

517+
port->erp_action.adapter = adapter;
518+
port->erp_action.port = port;
519+
515520
if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) {
516521
kfree(port);
517522
goto err_out;

drivers/s390/scsi/zfcp_erp.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
193193
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
194194
&zfcp_sdev->status);
195195
erp_action = &zfcp_sdev->erp_action;
196-
memset(erp_action, 0, sizeof(struct zfcp_erp_action));
197-
erp_action->port = port;
198-
erp_action->sdev = sdev;
196+
WARN_ON_ONCE(erp_action->port != port);
197+
WARN_ON_ONCE(erp_action->sdev != sdev);
199198
if (!(atomic_read(&zfcp_sdev->status) &
200199
ZFCP_STATUS_COMMON_RUNNING))
201200
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,
208207
zfcp_erp_action_dismiss_port(port);
209208
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
210209
erp_action = &port->erp_action;
211-
memset(erp_action, 0, sizeof(struct zfcp_erp_action));
212-
erp_action->port = port;
210+
WARN_ON_ONCE(erp_action->port != port);
211+
WARN_ON_ONCE(erp_action->sdev != NULL);
213212
if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
214213
act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
215214
break;
@@ -219,7 +218,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status,
219218
zfcp_erp_action_dismiss_adapter(adapter);
220219
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
221220
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);
223223
if (!(atomic_read(&adapter->status) &
224224
ZFCP_STATUS_COMMON_RUNNING))
225225
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,
229229
return NULL;
230230
}
231231

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;
233237
erp_action->action = need;
234238
erp_action->status = act_status;
235239

drivers/s390/scsi/zfcp_scsi.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,15 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev)
138138
struct zfcp_unit *unit;
139139
int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE;
140140

141+
zfcp_sdev->erp_action.adapter = adapter;
142+
zfcp_sdev->erp_action.sdev = sdev;
143+
141144
port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
142145
if (!port)
143146
return -ENXIO;
144147

148+
zfcp_sdev->erp_action.port = port;
149+
145150
unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev));
146151
if (unit)
147152
put_device(&unit->dev);

0 commit comments

Comments
 (0)