The Linux Kernel Mailing List
 help / color / mirror / Atom feed
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)


             reply	other threads:[~2026-07-09 15:06 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-09 15:06 Ibrahim Hashimov [this message]
2026-07-09 19:48 ` [PATCH v2] scsi: scsi_debug: fix REPORT ZONES alloc_len underflow OOB write 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  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox