From: Ibrahim Hashimov <security@auditcode.ai>
To: "James E . J . Bottomley" <James.Bottomley@HansenPartnership.com>,
"Martin K . Petersen" <martin.petersen@oracle.com>
Cc: linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
Subject: [PATCH] scsi: scsi_debug: fix REPORT ZONES alloc_len underflow OOB write
Date: Thu, 9 Jul 2026 17:06:31 +0200 [thread overview]
Message-ID: <20260709150631.45018-1-security@auditcode.ai> (raw)
resp_report_zones() only rejects a REPORT ZONES(16) CDB when the
requested allocation length (cmd[10..13], SBC/ZBC "ALLOCATION LENGTH")
is exactly zero:
alloc_len = get_unaligned_be32(cmd + 10);
if (alloc_len == 0)
return 0; /* not an error */
...
rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD);
arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN);
...
desc = arr + 64;
For any nonzero alloc_len in the range 1..63, `alloc_len - 64`
underflows (alloc_len and rep_max_zones are unsigned), turning
rep_max_zones into a huge value (~2^25 for typical small alloc_len).
Meanwhile arr is allocated with the raw, unvalidated alloc_len, so it
can be smaller than the 64-byte report header. desc is then set to
arr + 64, which already points past the end of the (too small)
allocation, and the per-zone descriptor loop:
if (nrz < rep_max_zones) {
desc[0] = zsp->z_type;
...
put_unaligned_be64((u64)zsp->z_wp, desc + 24);
desc += 64;
}
keeps writing 64-byte zone descriptors starting at that out-of-bounds
pointer and marching forward, because the inflated rep_max_zones no
longer bounds anything. A local CAP_SYS_RAWIO attacker who loads
scsi_debug in zoned mode (zbc=host-managed) and sends a REPORT ZONES
CDB with e.g. alloc_len=32 via SG_IO triggers a heap
slab-out-of-bounds write, confirmed under KASAN
("slab-out-of-bounds in resp_report_zones", writes landing at
arr+64, arr+128, ... i.e. exactly the desc[0]/desc[1]/
put_unaligned_be64(desc+8/16/24) sequence, stepping by 64 bytes per
iteration).
Every other REPORT-style/allocation-length-consuming handler in this
file floors alloc_len against its own header/descriptor size before
using it, e.g.:
resp_report_tgtpgs(): if (alloc_len < 4 || alloc_len > 0xffff) ...
resp_readcap16(): if (alloc_len < 24) return 0;
resp_get_stream_status(): if (alloc_len < 8) { ... }
resp_report_luns(): if (alloc_len < 4) { ... }
resp_report_zones() is missing the equivalent floor. Since the report
header itself is RZONES_DESC_HD (64) bytes, alloc_len must be at
least that before rep_max_zones is computed and before desc is walked
past the header. Add that check, rejecting an under-sized allocation
length the same way resp_get_stream_status() rejects an under-sized
one for the identical cmd[10..13] field (SDEB_IN_CDB, field offset
10) via mk_sense_invalid_fld() + check_condition_result. alloc_len==0
keeps its existing "not an error, nothing to report" fast path.
This bounds both the underflowing subtraction and the kzalloc() size
against the 64-byte header the function unconditionally writes,
without touching the per-zone descriptor loop itself.
Verified on a v6.19 KASAN build: issuing REPORT ZONES(16) via SG_IO
with alloc_len=32 against a scsi_debug zbc=host-managed device hits
the slab-out-of-bounds write in resp_report_zones() before this
patch, and the same command no longer triggers a KASAN report once
the floor check above is applied.
Fixes: 7db0e0c8190a ("scsi: scsi_debug: Fix buffer size of REPORT ZONES command")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Assisted-by: AuditCode-AI:2026.07
---
drivers/scsi/scsi_debug.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9d1c9c41d0f9..2fa887c65b61 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5911,6 +5911,11 @@ static int resp_report_zones(struct scsi_cmnd *scp,
return check_condition_result;
}
+ if (alloc_len < RZONES_DESC_HD) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 10, -1);
+ return check_condition_result;
+ }
+
rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD);
arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN);
--
2.50.1 (Apple Git-155)
next reply other threads:[~2026-07-09 15:06 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 15:06 Ibrahim Hashimov [this message]
2026-07-09 15:18 ` [PATCH] scsi: scsi_debug: fix REPORT ZONES alloc_len underflow OOB write sashiko-bot
2026-07-09 19:48 ` [PATCH v2] " Ibrahim Hashimov
2026-07-10 0:58 ` Damien Le Moal
2026-07-10 5:57 ` [PATCH v3] " Ibrahim Hashimov
2026-07-10 6:03 ` Damien Le Moal
2026-07-10 6:05 ` sashiko-bot
2026-07-10 14:37 ` Bart Van Assche
2026-07-12 18:36 ` Ibrahim Hashimov
2026-07-12 18:37 ` [PATCH v4] " Ibrahim Hashimov
2026-07-13 5:43 ` Damien Le Moal
2026-07-13 17:12 ` Bart Van Assche
2026-07-10 5:59 ` [PATCH v2] " Ibrahim Hashimov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260709150631.45018-1-security@auditcode.ai \
--to=security@auditcode.ai \
--cc=James.Bottomley@HansenPartnership.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=martin.petersen@oracle.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.