mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 09:20:13 -04:00
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:
committed by
Jens Axboe
parent
04ea1579bc
commit
a600051da1
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user