* [PATCH v6] scsi: zfcp: Fix out-of-bounds read of bogus status read buffer length in dbf
@ 2026-10-13 15:40 Ajaykumar Rajappa
2026-10-13 15:53 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Ajaykumar Rajappa @ 2026-10-13 15:40 UTC (permalink / raw)
To: linux-s390, sashiko-reviews; +Cc: Ajaykumar Rajappa
zfcp_dbf_hba_fsf_uss() and zfcp_dbf_san_in_els() derive the payload
length to trace from the channel supplied srb->length without checking
it against the size of the status read buffer. A bogus srb->length makes
the 16-bit pl_len wrap or exceed the payload array, so the trace code
may read beyond srb->payload.data. In addition, the u16 pl_len cannot
represent the original 32-bit srb->length, so the raw value is lost.
Add zfcp_dbf_srb_pl_len() to compute pl_len in one place, and use it in
both trace functions. Check bounds on the full 32-bit srb->length and
clamp the number of traced payload bytes to FSF_STATUS_READ_PAYLOAD_SIZE.
An oversized length is thereby not dropped, the readable part is still
traced.
If srb->length is neither 0 nor within header plus payload array size,
additionally write the raw 32-bit value as a separate "fsf_len" pay
record with the same req_id. This is done only in zfcp_dbf_hba_fsf_uss()
as it runs for every status read buffer, including incoming ELS which is
traced again by zfcp_dbf_san_in_els().
Payload length (pl_len) definition in the trace records:
- pl_len = srb->length - 64 for srb->length between 64 and 4096.
- pl_len = 0 if srb->length is 0.
- If an "fsf_len" record exists for the same req_id, pl_len is only
a 16-bit value derived from the bogus length.
Reconstructing srb->length without an "fsf_len" record:
- For pl_len between 0 and 4032, srb->length = pl_len + 64.
The exception is pl_len == 0, where the length is either 0 or 64;
a status_type of 0 indicates a length of 0.
- For pl_len above 4032, the original length was invalid and its raw
value is lost for good (PAY ring wrapped or not traced). zfcpdbf
prints a warning in this case.
Reconstructing srb->length with an "fsf_len" record:
- The "fsf_len" record holds the exact 32-bit length and takes
precedence over pl_len.
Fixes: a54ca0f62f95 ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.")
Fixes: 2c55b750a884 ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.")
Signed-off-by: Ajaykumar Rajappa <ajaykr@linux.ibm.com>
---
Changes in v6:
- Address sashiko-bot review comments.
- Update the patch subject and commit description to reflect that the
issue is an out-of-bounds read caused by a bogus SRB length and that
the fix also preserves channel-reported SRB lengths for
serviceability.
- Factor out common SRB payload-length handling into helper functions.
- Preserve out-of-range channel-reported SRB lengths in a dedicated
"fsf_len" pay record.
- Use the full 32-bit srb->length for bounds checking and clamp traced
payload data to the available payload area.
- Align HBA and SAN tracing paths to use the same payload-length and
payload-access handling.
- Expand documentation of pl_len semantics and SRB-length
reconstruction.
Changes in v5:
- Address Steffen's review comments regarding HBA/SAN trace servicability.
- Preserve HBA pl_len semantics so trace readers can always assume the
value-shifted subtraction.
- Retain the original channel-reported SRB length in SAN trace records.
- Use a separate capped length to control payload reads in the SAN path.
- Prevent payload reads when the reported buffer length does not reach
the payload area.
Changes in v4:
- Preserve the original channel-reported SRB length in SAN trace
records when no payload area exists.
- Distinguish between the recorded length and the payload length that
may be accessed using length/cap_length semantics.
- Use a capture length of zero to prevent payload access for malformed
status read buffers.
- Address both the underflow/OOB issue and the trace serviceability
concern in the same patch.
Changes in v3:
- Rework payload length handling for srb->length < pay_offset and
treat such cases as an empty payload.
- Prevent payload processing and scatterlist initialization when the
reported buffer length does not reach the payload area.
- Keep the fix focused on the underflow/OOB issue and preserve the
existing tracing behavior for valid payloads.
Changes in v2:
- Preserve anomalous short srb->length values by capping payload lengths
to srb->length instead of forcing them to 0.
- Introduce a local pay_offs₹et variable to avoid repeated offsetof()
Changes on the git hub can be found at:
https://github.ibm.com/Ajay-Kumar-K-R/linux/tree/zfcp-fix-srb-length-underflow-v6
---
drivers/s390/scsi/zfcp_dbf.c | 104 +++++++++++++++++++++++++++++++----
1 file changed, 92 insertions(+), 12 deletions(-)
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 81fb8af408e9..c53c0efdf875 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -65,6 +65,48 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area,
spin_unlock(&dbf->pay_lock);
}
+/**
+ * zfcp_dbf_srb_pl_len - payload length to be traced for a status read buffer
+ * @srb: status read buffer as received from the channel
+ *
+ * A length of 0 (buffer not filled, e.g. dismissed request) means no payload.
+ * Otherwise the payload length is srb->length without the header. A bogus
+ * srb->length below the header size wraps to a large value and is thereby
+ * distinguishable from a genuine empty payload.
+ */
+static inline u16 zfcp_dbf_srb_pl_len(struct fsf_status_read_buffer *srb)
+{
+ if (!srb->length)
+ return 0;
+ return (u16)(srb->length -
+ offsetof(struct fsf_status_read_buffer, payload));
+}
+
+/**
+ * zfcp_dbf_srb_len_trace_if_bogus - trace raw SRB length if out of range
+ * @dbf: debug feature of the adapter
+ * @srb: status read buffer as received from the channel
+ * @req_id: request ID used to correlate with the HBA and SAN trace records
+ *
+ * The u16 pl_len in the trace records cannot hold the full 32-bit
+ * srb->length. Unless the length is 0 or within the header plus payload
+ * array size, write the raw 32-bit value as a separate "fsf_len" pay record.
+ */
+static void zfcp_dbf_srb_len_trace_if_bogus(struct zfcp_dbf *dbf,
+ struct fsf_status_read_buffer *srb,
+ u64 req_id)
+{
+ u32 raw_len = srb->length;
+
+ if (!raw_len ||
+ (raw_len >= offsetof(struct fsf_status_read_buffer, payload) &&
+ raw_len <= sizeof(*srb)))
+ return;
+
+ zfcp_dbf_pl_write(dbf, &raw_len, sizeof(raw_len), "fsf_len", req_id,
+ ZFCP_DBF_PAY_LEVEL);
+}
+
/**
* zfcp_dbf_hba_fsf_res - trace event for fsf responses
* @tag: tag indicating which kind of FSF response has been received
@@ -223,6 +265,7 @@ void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req)
struct zfcp_dbf_hba *rec = &dbf->hba_buf;
static int const level = 2;
unsigned long flags;
+ const u32 pay_offset = offsetof(struct fsf_status_read_buffer, payload);
if (unlikely(!debug_level_enabled(dbf->hba, level)))
return;
@@ -253,13 +296,30 @@ void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req)
rec->u.uss.s_id = ntoh24(srb->s_id);
memcpy(&rec->u.uss.res4, &srb->res4, sizeof(rec->u.uss.res4));
- /* status read buffer payload length */
- rec->pl_len = (!srb->length) ? 0 : srb->length -
- offsetof(struct fsf_status_read_buffer, payload);
+ /* pl_len is the payload length (srb->length - pay_offset) as long as
+ * srb->length is valid. For a bogus srb->length it is only derived
+ * from it and the raw length is traced as "fsf_len" below.
+ */
+ rec->pl_len = zfcp_dbf_srb_pl_len(srb);
+
+ /* Only access payload bytes when srb->length actually covers them.
+ * Bounds are checked on the full 32-bit value (before any u16
+ * truncation). An oversized srb->length is not dropped: the payload
+ * is traced up to the end of the payload array (diagnostic value).
+ */
+ if (srb->length > pay_offset) {
+ u32 cap_len = srb->length - pay_offset;
- if (rec->pl_len)
- zfcp_dbf_pl_write(dbf, srb->payload.data, rec->pl_len,
- "fsf_uss", req->req_id, ZFCP_DBF_PAY_LEVEL);
+ if (cap_len > FSF_STATUS_READ_PAYLOAD_SIZE)
+ cap_len = FSF_STATUS_READ_PAYLOAD_SIZE;
+ zfcp_dbf_pl_write(dbf, srb->payload.data, cap_len, "fsf_uss",
+ req->req_id, ZFCP_DBF_PAY_LEVEL);
+ }
+ /* pl_len is only 16 bit: keep the raw length for out-of-range cases.
+ * Done only here as this runs for every status read buffer, including
+ * incoming ELS which is traced again by zfcp_dbf_san_in_els().
+ */
+ zfcp_dbf_srb_len_trace_if_bogus(dbf, srb, req->req_id);
log:
debug_event(dbf->hba, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->hba_lock, flags);
@@ -714,17 +774,37 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
struct zfcp_dbf *dbf = fsf->adapter->dbf;
struct fsf_status_read_buffer *srb =
(struct fsf_status_read_buffer *) fsf->data;
- u16 length;
+ u16 pl_len, cap_length = 0;
struct scatterlist sg;
+ const u32 pay_offset = offsetof(struct fsf_status_read_buffer, payload);
if (unlikely(!debug_level_enabled(dbf->san, ZFCP_DBF_SAN_LEVEL)))
return;
- length = (u16)(srb->length -
- offsetof(struct fsf_status_read_buffer, payload));
- sg_init_one(&sg, srb->payload.data, length);
- zfcp_dbf_san(tag, dbf, "san_els", &sg, ZFCP_DBF_SAN_ELS, length,
- fsf->req_id, ntoh24(srb->d_id), length);
+ /* pl_len is the payload length as reported by the channel (same
+ * mapping as in zfcp_dbf_hba_fsf_uss()). It is passed as "len" so
+ * that zfcp_dbf_san() can skip the separate pay record when the
+ * payload fits into rec->payload. The raw length of a bogus
+ * srb->length is traced as "fsf_len" by zfcp_dbf_hba_fsf_uss().
+ *
+ * cap_length is the number of payload bytes that may actually be
+ * read: bounds are checked on the full 32-bit srb->length and an
+ * oversized value is clamped to the payload array size, so the
+ * readable part is still traced.
+ */
+ pl_len = zfcp_dbf_srb_pl_len(srb);
+ if (srb->length > pay_offset) {
+ u32 cap = srb->length - pay_offset;
+
+ if (cap > FSF_STATUS_READ_PAYLOAD_SIZE)
+ cap = FSF_STATUS_READ_PAYLOAD_SIZE;
+ cap_length = cap;
+ sg_init_one(&sg, srb->payload.data, cap_length);
+ }
+
+ zfcp_dbf_san(tag, dbf, "san_els", cap_length ? &sg : NULL,
+ ZFCP_DBF_SAN_ELS, pl_len, fsf->req_id, ntoh24(srb->d_id),
+ cap_length);
}
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v6] scsi: zfcp: Fix out-of-bounds read of bogus status read buffer length in dbf
2026-10-13 15:40 [PATCH v6] scsi: zfcp: Fix out-of-bounds read of bogus status read buffer length in dbf Ajaykumar Rajappa
@ 2026-10-13 15:53 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-10-13 15:53 UTC (permalink / raw)
To: Ajaykumar Rajappa, linux-s390
Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Christian Borntraeger
> zfcp_dbf_hba_fsf_uss() and zfcp_dbf_san_in_els() derive the payload
> length to trace from the channel supplied srb->length without checking
> it against the size of the status read buffer. A bogus srb->length makes
> the 16-bit pl_len wrap or exceed the payload array, so the trace code
> may read beyond srb->payload.data. In addition, the u16 pl_len cannot
> represent the original 32-bit srb->length, so the raw value is lost.
>
> Add zfcp_dbf_srb_pl_len() to compute pl_len in one place, and use it in
> both trace functions. Check bounds on the full 32-bit srb->length and
> clamp the number of traced payload bytes to FSF_STATUS_READ_PAYLOAD_SIZE.
> An oversized length is thereby not dropped, the readable part is still
> traced.
>
> If srb->length is neither 0 nor within header plus payload array size,
> additionally write the raw 32-bit value as a separate "fsf_len" pay
> [ ... ]
> precedence over pl_len.
>
> Fixes: a54ca0f62f95 ("[SCSI] zfcp: Redesign of the debug tracing for HBA records.")
> Fixes: 2c55b750a884 ("[SCSI] zfcp: Redesign of the debug tracing for SAN records.")
> Signed-off-by: Ajaykumar Rajappa <ajaykr@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013154041.393716-1-ajaykr@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-13 15:53 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 15:40 [PATCH v6] scsi: zfcp: Fix out-of-bounds read of bogus status read buffer length in dbf Ajaykumar Rajappa
2026-10-13 15:53 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.