Re: [PATCH v7 11/19] s390/dasd: Add dasd_eckd_build_cp_tpm_writefulltrack()

[email protected] Sat, 01 Aug 2026 18:17:10 +0000
Newsgroups org.kernel.vger.linux-s390
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [High] The WRITE_FULL_TRACK channel program emits one ECKD COUNT record per physical `bvec` segment fragment rather than per logical block, corrupting disk geometry, truncating user data, and overflowing TIDAW allocations.
- [High] Missing `bio_has_data()` guard before iterating over request segments in `dasd_eckd_build_cp_tpm_writefulltrack` causes a NULL pointer dereference on payload-less requests.
- [High] Negative bit shift in `prepare_itcw()` when track count exceeds 16 causes undefined behavior.

Pre-existing issues:
- [High] Preexisting functions `dasd_eckd_build_cp_tpm_track` and `dasd_eckd_build_cp_cmd_track` use `rq_data_dir()` to guard data access, failing to exclude dataless write operations and causing NULL pointer dereferences.
--

--- Patch [11]: [PATCH v7 11/19] s390/dasd: Add dasd_eckd_build_cp_tpm_writefulltrack() ---
commit 194da1c99392fbbd1e70e7463ef11e36aaa6e8ba
Author: Stefan Haberland <[email protected]>

s390/dasd: Add dasd_eckd_build_cp_tpm_writefulltrack()

This commit adds a channel program builder for WRITE_FULL_TRACK requests.
It creates an ITCW with a TIDAW list for track formatting and writing on
ESE devices.

> diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
> index b54d1858db160..475c0aca4a5ae 100644
> --- a/drivers/s390/block/dasd_eckd.c
> +++ b/drivers/s390/block/dasd_eckd.c
> @@ -4742,6 +4750,340 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
>  	return ERR_PTR(ret);
>  }

[Severity: High]
This is a pre-existing issue, but functions like
dasd_eckd_build_cp_tpm_track() and dasd_eckd_build_cp_cmd_track() appear to
use rq_data_dir() to guard data access.

Does this safely exclude dataless write operations? If a dataless write like
REQ_OP_DISCARD or REQ_OP_WRITE_ZEROES is submitted, will the call to
rq_for_each_segment() cause a NULL pointer dereference on bi_io_vec? A
bio_has_data() guard might be required here to strictly verify the payload
before accessing data segments.

> +static __maybe_unused struct dasd_ccw_req *
> +dasd_eckd_build_cp_tpm_writefulltrack(struct dasd_device *startdev,

[ ... ]

> +	trkcount = last_trk - first_trk + 1;
> +	ctidaw = trkcount * 2 * (blk_per_trk + 2);

[ ... ]

> +	if (prepare_itcw(itcw, first_trk, last_trk,
> +			 cmd, basedev, startdev,
> +			 0,
> +			 trkcount, blksize,
> +			 datasize,
> +			 tlf,
> +			 blk_per_trk) == -EAGAIN) {

[Severity: High]
Can this result in undefined behavior due to a negative bit shift?

The variable trkcount is calculated from the request dimensions and passed as
the count argument to prepare_itcw(). If trkcount exceeds 16, does the
operation lredata->extended_parameter[1] <<= (16 - count) inside
prepare_itcw() perform a shift by a negative amount?

[ ... ]

> +	/* process data records */
> +	rec = first_offs + 1;
> +	rec_count = 0;
> +	rq_for_each_segment(bv, req, iter) {

[Severity: High]
Does this iteration safely handle payload-less requests?

If a dataless request, such as REQ_OP_WRITE_ZEROES or REQ_OP_DISCARD, is
processed, the bi_io_vec will be NULL. Will executing rq_for_each_segment()
here cause a NULL pointer dereference? Should this iteration be guarded by a
check to bio_has_data() to ensure the payload is present?

> +		dst = bvec_virt(&bv);
> +		seg_len = bv.bv_len;
> +		while (seg_len) {

[ ... ]

> +			/*
> +			 * One count + data record per block: a bvec segment can
> +			 * be up to a page, so clamp to blksize - otherwise the
> +			 * count field would describe one oversized record instead
> +			 * of several blksize ones for sub-page block sizes.
> +			 */
> +			part_len = min(seg_len, len_to_track_end);
> +			part_len = min(part_len, blksize);
> +			seg_len -= part_len;
> +			len_to_track_end -= part_len;

[ ... ]

> +			last_tidaw = add_track_record(itcw, &filldata, &fillsize,
> +						      cyl, head, rec, dst, part_len);

[Severity: High]
Does this emit a separate ECKD COUNT record for each physical memory fragment
rather than per logical block?

If a logical block spans multiple physical pages and is split into multiple
bvec segments by the block layer, such as an unaligned 4K block crossing a
page boundary, it appears add_track_record() is called unconditionally per
physical fragment.

Will this cause the function to emit two smaller COUNT records instead of a
single 4096-byte COUNT record? This seems like it could break track geometry,
overflow the pre-calculated ctidaw limit by generating additional TIDAWs, and
prematurely increment the logical record counters.

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=11