* [PATCH 1/3] s390/dasd: Re-initialize RCD buffer with "V1.0" before every start
2026-10-23 9:21 [PATCH 0/3] s390/dasd: Fix BIODASDCHECKFMT issues and RCD retry handling Stefan Haberland
@ 2026-10-23 9:21 ` Stefan Haberland
2026-10-23 9:21 ` [PATCH 2/3] s390/dasd: Fix wrong free and refcount leak in build_check_tcw error path Stefan Haberland
2026-10-23 9:21 ` [PATCH 3/3] s390/dasd: Don't trust userspace-supplied result field in format check Stefan Haberland
2 siblings, 0 replies; 4+ messages in thread
From: Stefan Haberland @ 2026-10-23 9:21 UTC (permalink / raw)
To: linux-s390; +Cc: Jan Hoeppner, Eduard Shishkin
Under z/VM the Read Configuration Data (RCD) buffer has to start with
EBCDIC "V1.0" to request the virtual device SNEQ that carries the
minidisk UIT. The request and the response share the same buffer:
CP evaluates the magic on the way out and the RCD data is written into
the same buffer on the way back.
The magic is only written when the request is built and again in
read_conf_cb() when the original request fails.
Several restart paths do not run that callback:
- ERP use the same buffer and if the same error occurs again,
the ERP restarts the existing ERP request;
if a different error occurs, it adds another ERP request.
Neither re-initializes the magic.
- Terminated (timed out) ERP requests, killed requests and requests
requeued after the last path was gone are restarted without it.
- dasd_sleep_on_immediately() overwrites cqr->callback, so read_conf_cb()
never ran for dasd_eckd_read_conf_immediately(), and its fastpath
retries in dasd_int_handler() do not call a callback either.
If such a restart succeeds, z/VM omits the virtual device SNEQ, the UID
of that path is built without the UIT and the paths are reported as
"Not all channel paths lead to the same device" although they lead to
the same minidisk.
Every start of a request, whatever path led to it, goes through
dasd_start_IO(). Add an optional prepare_io function to the request
that dasd_start_IO() calls before every start of the request and of
its ERP requests, and use it to write the magic for RCD requests.
The ERP handling stays unchanged. read_conf_cb() is no longer needed
and is removed, which also gets rid of its memset() that only cleared
a single byte.
Fixes: 5915a873fcb1 ("[S390] dasd: re-initialize read_conf buffer for retries")
Cc: stable@vger.kernel.org
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
---
drivers/s390/block/dasd.c | 1 +
drivers/s390/block/dasd_eckd.c | 52 ++++++++++++----------------------
drivers/s390/block/dasd_int.h | 15 ++++++++++
3 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index d74ef737ae26..3a55c937d0d6 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1396,6 +1396,7 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
cqr->status = DASD_CQR_ERROR;
return -EIO;
}
+ dasd_start_IO_prepare(cqr);
cqr->startclk = get_tod_clock();
cqr->starttime = jiffies;
cqr->retries--;
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 970b83467276..e910d6b35f0f 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -845,20 +845,29 @@ static int dasd_eckd_compare_path_uid(struct dasd_device *device,
return memcmp(&device_uid, &path_uid, sizeof(struct dasd_uid));
}
+/*
+ * The RCD buffer has to start with EBCDIC "V1.0" to show support for
+ * virtual device SNEQ. The RCD data is written into the same buffer, so
+ * a failed attempt may already have overwritten the magic. Re-initialize
+ * it before every start, including restarts by ERP requests.
+ */
+static void dasd_eckd_rcd_prepare_io(struct dasd_ccw_req *cqr)
+{
+ struct ccw1 *ccw = cqr->cpaddr;
+ __u8 *rcd_buffer = dma32_to_virt(ccw->cda);
+
+ rcd_buffer[0] = 0xE5;
+ rcd_buffer[1] = 0xF1;
+ rcd_buffer[2] = 0x4B;
+ rcd_buffer[3] = 0xF0;
+}
+
static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
struct dasd_ccw_req *cqr,
__u8 *rcd_buffer,
__u8 lpm)
{
struct ccw1 *ccw;
- /*
- * buffer has to start with EBCDIC "V1.0" to show
- * support for virtual device SNEQ
- */
- rcd_buffer[0] = 0xE5;
- rcd_buffer[1] = 0xF1;
- rcd_buffer[2] = 0x4B;
- rcd_buffer[3] = 0xF0;
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RCD;
@@ -875,33 +884,10 @@ static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
cqr->retries = 256;
cqr->buildclk = get_tod_clock();
cqr->status = DASD_CQR_FILLED;
+ cqr->prepare_io = dasd_eckd_rcd_prepare_io;
set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
}
-/*
- * Wakeup helper for read_conf
- * if the cqr is not done and needs some error recovery
- * the buffer has to be re-initialized with the EBCDIC "V1.0"
- * to show support for virtual device SNEQ
- */
-static void read_conf_cb(struct dasd_ccw_req *cqr, void *data)
-{
- struct ccw1 *ccw;
- __u8 *rcd_buffer;
-
- if (cqr->status != DASD_CQR_DONE) {
- ccw = cqr->cpaddr;
- rcd_buffer = dma32_to_virt(ccw->cda);
- memset(rcd_buffer, 0, sizeof(*rcd_buffer));
-
- rcd_buffer[0] = 0xE5;
- rcd_buffer[1] = 0xF1;
- rcd_buffer[2] = 0x4B;
- rcd_buffer[3] = 0xF0;
- }
- dasd_wakeup_cb(cqr, data);
-}
-
static int dasd_eckd_read_conf_immediately(struct dasd_device *device,
struct dasd_ccw_req *cqr,
__u8 *rcd_buffer,
@@ -921,7 +907,6 @@ static int dasd_eckd_read_conf_immediately(struct dasd_device *device,
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);
cqr->retries = 5;
- cqr->callback = read_conf_cb;
rc = dasd_sleep_on_immediately(cqr);
return rc;
}
@@ -959,7 +944,6 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
goto out_error;
}
dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
- cqr->callback = read_conf_cb;
ret = dasd_sleep_on(cqr);
/*
* on success we update the user input parms
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 7b154cd43625..1ff5188f0b97 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -147,6 +147,8 @@ struct dasd_ccw_req {
void *data; /* pointer to data area */
struct irb irb; /* device status in case of an error */
struct dasd_ccw_req *refers; /* ERP-chain queueing. */
+ /* optional prepare function called before every start_io, must not sleep */
+ void (*prepare_io)(struct dasd_ccw_req *cqr);
void *function; /* originating ERP action */
void *mem_chunk;
@@ -891,6 +893,19 @@ static inline void *dasd_get_callback_data(struct dasd_ccw_req *cqr)
return cqr->callback_data;
}
+/*
+ * Walk down the ERP chain to the original CQR and call its
+ * prepare_io function if set.
+ */
+static inline void dasd_start_IO_prepare(struct dasd_ccw_req *cqr)
+{
+ /* ERP requests use the prepare function of the original request */
+ while (cqr->refers)
+ cqr = cqr->refers;
+ if (cqr->prepare_io)
+ cqr->prepare_io(cqr);
+}
+
static inline bool dasd_req_conflict(struct dasd_ccw_req *cqr1,
struct dasd_ccw_req *cqr2)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 2/3] s390/dasd: Fix wrong free and refcount leak in build_check_tcw error path
2026-10-23 9:21 [PATCH 0/3] s390/dasd: Fix BIODASDCHECKFMT issues and RCD retry handling Stefan Haberland
2026-10-23 9:21 ` [PATCH 1/3] s390/dasd: Re-initialize RCD buffer with "V1.0" before every start Stefan Haberland
@ 2026-10-23 9:21 ` Stefan Haberland
2026-10-23 9:21 ` [PATCH 3/3] s390/dasd: Don't trust userspace-supplied result field in format check Stefan Haberland
2 siblings, 0 replies; 4+ messages in thread
From: Stefan Haberland @ 2026-10-23 9:21 UTC (permalink / raw)
To: linux-s390; +Cc: Jan Hoeppner, Eduard Shishkin
dasd_eckd_build_check_tcw() allocates its request with
dasd_fmalloc_request(), but its out_err path frees it with
dasd_sfree_request(). The latter uses a different pool and therefor,
leading to invalid pointer arithmetic and a crash.
Use the matching dasd_ffree_request() instead.
The same error path also leaves start_priv->count incremented
without ever decrementing it again.
This leaks a permanent reference on every build failure.
Decrement it before freeing the request.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260722103704.1709668-1-sth@linux.ibm.com
Fixes: 8fd575200db5 ("s390/dasd: Add new ioctl BIODASDCHECKFMT")
Cc: stable@vger.kernel.org #4.7
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
---
drivers/s390/block/dasd_eckd.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index e910d6b35f0f..687350504c3e 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -2764,7 +2764,8 @@ dasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata,
return cqr;
out_err:
- dasd_sfree_request(cqr, startdev);
+ start_priv->count--;
+ dasd_ffree_request(cqr, startdev);
return ERR_PTR(rc);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 3/3] s390/dasd: Don't trust userspace-supplied result field in format check
2026-10-23 9:21 [PATCH 0/3] s390/dasd: Fix BIODASDCHECKFMT issues and RCD retry handling Stefan Haberland
2026-10-23 9:21 ` [PATCH 1/3] s390/dasd: Re-initialize RCD buffer with "V1.0" before every start Stefan Haberland
2026-10-23 9:21 ` [PATCH 2/3] s390/dasd: Fix wrong free and refcount leak in build_check_tcw error path Stefan Haberland
@ 2026-10-23 9:21 ` Stefan Haberland
2 siblings, 0 replies; 4+ messages in thread
From: Stefan Haberland @ 2026-10-23 9:21 UTC (permalink / raw)
To: linux-s390; +Cc: Jan Hoeppner, Eduard Shishkin
BIODASDCHECKFMT used to copy the entire format_check_t struct from
userspace, including the result/unit/rec/num_records/blksize/
key_length fields that are output fields only.
dasd_eckd_format_evaluate_tracks() relies on cdata->result being zero
to decide whether to apply an index correction (i--/pos--) after a
successful check. A caller that pre-sets result to a non-zero value
can suppress that correction, leaving pos advanced past the last valid
entry; the (partially out-of-bounds) fmt_buffer[pos] fields are then
copied back to userspace, leaking heap contents.
Only expect is an Input field; copy just that member in and zero
the rest of cdata.
Fixes: 8fd575200db5 ("s390/dasd: Add new ioctl BIODASDCHECKFMT")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260722103704.1709668-1-sth@linux.ibm.com
Cc: stable@vger.kernel.org #4.7
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
---
drivers/s390/block/dasd_ioctl.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index e5b8b413f5ab..2f2ec2bea333 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -303,7 +303,9 @@ static int dasd_ioctl_check_format(struct block_device *bdev, void __user *argp)
goto out_err;
}
- if (copy_from_user(&cdata, argp, sizeof(cdata))) {
+ /* copy only input fields, clear the rest */
+ memset(&cdata, 0, sizeof(cdata));
+ if (copy_from_user(&cdata.expect, argp, sizeof(cdata.expect))) {
rc = -EFAULT;
goto out_err;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread