s390/dasd: Read cached unit address and LSS in the CCW build path

The CCW build path (prefix_LRE, the full-track prefix and dso_ras) read the
base address and LSS straight from conf.ned. That buffer is freed and
reallocated by the reload worker (do_reload_device - dasd_eckd_read_conf -
dasd_eckd_clear_conf_data), so a configuration change concurrent with I/O
can free conf.ned while a request is being built.
Use-after-free reported by KASAN in prefix_LRE.

Read the cached copies instead.
The unit address is already kept in uid.real_unit_addr, and the LSS is now
cached in ned_lss. Both are refreshed under the ccwdev lock in
dasd_eckd_generate_uid whenever the configuration is (re)read.
Also fix for prepare for read subsystem data (prssd) users.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Link: https://patch.msgid.link/20260805111612.1285190-20-sth@linux.ibm.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Stefan Haberland
2026-08-05 13:16:12 +02:00
committed by Jens Axboe
parent 04ea1579bc
commit a600051da1
2 changed files with 24 additions and 10 deletions

View File

@@ -588,8 +588,9 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
return -EINVAL;
}
pfxdata->format = format;
pfxdata->base_address = basepriv->conf.ned->unit_addr;
pfxdata->base_lss = basepriv->conf.ned->ID;
/* cached copies - conf.ned may be freed under us by the reload worker */
pfxdata->base_address = READ_ONCE(basepriv->ned_ua);
pfxdata->base_lss = READ_ONCE(basepriv->ned_lss);
pfxdata->validity.define_extent = 1;
/* private uid is kept up to date, conf_data may be outdated */
@@ -806,6 +807,9 @@ static int dasd_eckd_generate_uid(struct dasd_device *device)
return -ENODEV;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
create_uid(&private->conf, &private->uid);
/* cache LSS and unit address for the lockless CCW-build path */
WRITE_ONCE(private->ned_lss, private->conf.ned->ID);
WRITE_ONCE(private->ned_ua, private->conf.ned->unit_addr);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
return 0;
}
@@ -1631,8 +1635,8 @@ static int dasd_eckd_read_vol_info(struct dasd_device *device)
prssdp = cqr->data;
prssdp->order = PSF_ORDER_PRSSD;
prssdp->suborder = PSF_SUBORDER_VSQ; /* Volume Storage Query */
prssdp->lss = private->conf.ned->ID;
prssdp->volume = private->conf.ned->unit_addr;
prssdp->lss = READ_ONCE(private->ned_lss);
prssdp->volume = READ_ONCE(private->ned_ua);
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_PSF;
@@ -4247,8 +4251,9 @@ dasd_eckd_dso_ras(struct dasd_device *device, struct dasd_block *block,
if (!req && features->feature[56] & 0x01 && !copy_relation)
ras_data->op_flags.guarantee_init = 1;
ras_data->lss = private->conf.ned->ID;
ras_data->dev_addr = private->conf.ned->unit_addr;
/* cached copies - conf.ned may be freed under us by the reload worker */
ras_data->lss = READ_ONCE(private->ned_lss);
ras_data->dev_addr = READ_ONCE(private->ned_ua);
ras_data->nr_exts = nr_exts;
if (by_extent) {
@@ -4819,8 +4824,9 @@ static int prepare_itcw(struct itcw *itcw,
lredata = &pfxdata->locate_record;
pfxdata->format = 1; /* PFX with LRE */
pfxdata->base_address = basepriv->conf.ned->unit_addr;
pfxdata->base_lss = basepriv->conf.ned->ID;
/* cached copies - conf.ned may be freed under us by the reload worker */
pfxdata->base_address = READ_ONCE(basepriv->ned_ua);
pfxdata->base_lss = READ_ONCE(basepriv->ned_lss);
pfxdata->validity.define_extent = 1;
/* private uid is kept up to date, conf_data may be outdated */
@@ -6902,8 +6908,8 @@ static int dasd_eckd_query_host_access(struct dasd_device *device,
prssdp->order = PSF_ORDER_PRSSD;
prssdp->suborder = PSF_SUBORDER_QHA; /* query host access */
/* LSS and Volume that will be queried */
prssdp->lss = private->conf.ned->ID;
prssdp->volume = private->conf.ned->unit_addr;
prssdp->lss = READ_ONCE(private->ned_lss);
prssdp->volume = READ_ONCE(private->ned_ua);
/* all other bytes of prssdp must be zero */
ccw = cqr->cpaddr;

View File

@@ -736,6 +736,14 @@ struct dasd_eckd_private {
/* alias management */
struct dasd_uid uid;
/*
* Cached copies of conf.ned->ID (the LSS) and conf.ned->unit_addr,
* refreshed under ccwdev_lock. Kept outside uid because create_uid()
* memsets uid before repopulating it, which would expose a transient
* zero to the lockless CCW-build readers.
*/
__u8 ned_lss;
__u8 ned_ua;
struct alias_pav_group *pavgroup;
struct alias_lcu *lcu;
int count;