* [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation
@ 2026-09-28 7:21 Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 01/12] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
` (11 more replies)
0 siblings, 12 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
This series fixes scsi_debug with regards to writes to a ZBC drive, and
to WRITE ATOMIC (16). Each patch describes the problem that it fixes.
Two configurations are refused rather than emulated, and those two
patches come first, as the rest of the series relies on what they
exclude: a zoned device whose lowest aligned LBA is not zero, which no
host writing in units of the reported zone write granularity could ever
write, and atomic writes on a zoned device, which no drive supports.
Little of this is reachable in a default configuration. Most of it needs
physblk_exp, atomic_wr or logical block provisioning to be set, or an
initiator that does not provide a data buffer matching the transfer
length of the command.
The series is based on 7.4/scsi-staging rather than 7.3/scsi-fixes (as
Damien suggested on v1) to avoid a build failure that would have
happened if this series was simply merged with linux-next:
error: too many arguments to function 'mk_sense_buffer'
Tested on a zoned scsi_debug device with 512 byte logical blocks and a
4096 byte physical block, and on a device that is not zoned for the
WRITE ATOMIC (16) patches. The notes below each patch describe what was
observed before and after it.
Changes since v9:
- Patch 5 is new. A WRITE SCATTERED (16) range of 4 GiB or more wrapped
the 32-bit offset into the data-out buffer, so the ranges that followed
were written from the wrong part of it. Sashiko pointed out that the
wrap also gave a wrong residual.
- Patch 6 cites the definition of the residual in
Documentation/scsi/scsi_mid_low_api.rst.
Niklas Cassel (12):
scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned
LBA
scsi: scsi_debug: Make atomic writes and ZBC emulation mutually
exclusive
scsi: scsi_debug: Take the zone metadata lock before the data lock
scsi: scsi_debug: Evaluate scsi_debug_lbp() only once
scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets
scsi: scsi_debug: Report the residual of a write
scsi: scsi_debug: Enforce physical block alignment of zoned writes
scsi: scsi_debug: Do not write a partial physical block to a zoned
device
scsi: scsi_debug: Advance the write pointer over the data written
scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
scsi: scsi_debug: Map the region written by WRITE ATOMIC (16)
scsi: scsi_debug: Validate the access parameters of WRITE ATOMIC (16)
drivers/scsi/scsi_debug.c | 132 ++++++++++++++++++++++++++++++--------
1 file changed, 106 insertions(+), 26 deletions(-)
base-commit: f09d2c7485b32adb82336d0d748935c8237a649e
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v10 01/12] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 02/12] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive Niklas Cassel
` (10 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
scsi_debug reports the lowest_aligned module parameter in the LOWEST
ALIGNED LOGICAL BLOCK ADDRESS field, see SBC-6 r02 (T10/BSR INCITS 587),
5.20.2, but nothing else in the driver acts on it.
On a zoned device a non-zero value puts the start of every zone in the
middle of a physical block. ZBC requires a write to a sequential write
required zone to end on a physical block boundary, so a host writing in
units of the reported zone write granularity could never write such a
zone at all.
Refuse the combination rather than emulate a device that cannot be used.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested by loading the module with each combination:
zbc=managed lowest_aligned=8 refused, -EINVAL
zbc=aware lowest_aligned=8 refused, -EINVAL
zbc=managed lowest_aligned=0 loads
lowest_aligned=8 loads, as the device is not zoned
---
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 6941809dfdb7..c664966e95cb 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -8734,6 +8734,11 @@ static int __init scsi_debug_init(void)
sdebug_dev_size_mb = DEF_ZBC_DEV_SIZE_MB;
}
+ if (sdeb_zbc_in_use && sdebug_lowest_aligned) {
+ pr_err("lowest_aligned is not supported by a zoned device\n");
+ return -EINVAL;
+ }
+
if (sdebug_dev_size_mb == DEF_DEV_SIZE_PRE_INIT)
sdebug_dev_size_mb = DEF_DEV_SIZE_MB;
if (sdebug_dev_size_mb < 1)
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 02/12] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 01/12] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 03/12] scsi: scsi_debug: Take the zone metadata lock before the data lock Niklas Cassel
` (9 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
There are no SMR drives that support atomic writes, so emulating a
device with both emulates something a user cannot encounter, and means
maintaining the zone handling of a command that no zoned device
implements.
Nothing in the standards forbids the combination, and for a host managed
drive whose logical block size equals its physical block size there is
no problem in it either: the vendor only has to give atomic writes an
alignment that makes sense with regards to ZBC. This is a choice not to
complicate scsi_debug.
Refuse the combination at initialisation. It can be changed back if such
drives ever appear.
Suggested-by: Damien Le Moal <dlemoal@kernel.org>
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested by loading the module with each combination:
zbc=managed atomic_wr=1 refused, -EINVAL
zbc=aware atomic_wr=1 refused, -EINVAL
zbc=managed loads
atomic_wr=1 loads
---
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 c664966e95cb..8e45a57ae406 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -8739,6 +8739,11 @@ static int __init scsi_debug_init(void)
return -EINVAL;
}
+ if (sdeb_zbc_in_use && sdebug_atomic_wr) {
+ pr_err("atomic_wr is not supported by a zoned device\n");
+ return -EINVAL;
+ }
+
if (sdebug_dev_size_mb == DEF_DEV_SIZE_PRE_INIT)
sdebug_dev_size_mb = DEF_DEV_SIZE_MB;
if (sdebug_dev_size_mb < 1)
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 03/12] scsi: scsi_debug: Take the zone metadata lock before the data lock
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 01/12] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 02/12] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 04/12] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once Niklas Cassel
` (8 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_comp_write() takes the data lock and then the zone metadata lock,
while every other command that takes both takes the metadata lock first
and reaches the data lock through do_device_access(). A COMPARE AND
WRITE and any other write to the same store can therefore deadlock each
other.
Take the locks in the same order as everywhere else.
Correct the comment above map_region() while here: the provisioning map
is covered by the metadata lock rather than the data lock, which is why
resp_unmap() takes only the metadata lock.
Assisted-by: LLM
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 dev_size_mb=128 lbpu=1 lbpws=1
COMPARE AND WRITE completes, and a READ(16) of the block that it wrote
returns, so the reordered locks are still taken and released correctly.
The deadlock itself was not reproduced. This kernel is built without
lockdep, and the window needs a COMPARE AND WRITE and another write to
the same store to interleave between the two acquisitions. Found by
review.
---
drivers/scsi/scsi_debug.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 8e45a57ae406..18aefe83b7b6 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5575,8 +5575,8 @@ static int resp_comp_write(struct scsi_cmnd *scp,
"indicated=%u, IO sent=%d bytes\n", my_name,
dnum * lb_size, ret);
- sdeb_data_write_lock(sip);
sdeb_meta_write_lock(sip);
+ sdeb_data_write_lock(sip);
if (!comp_write_worker(sip, lba, num, arr, false)) {
mk_sense_buffer(scp, MISCOMPARE,
MISCOMPARE_DURING_VERIFY_OPERATION);
@@ -5584,12 +5584,12 @@ static int resp_comp_write(struct scsi_cmnd *scp,
goto cleanup_unlock;
}
- /* Cover sip->map_storep (which map_region()) sets with data lock */
+ /* Cover sip->map_storep (which map_region() sets) with the meta lock */
if (scsi_debug_lbp())
map_region(sip, lba, num);
cleanup_unlock:
- sdeb_meta_write_unlock(sip);
sdeb_data_write_unlock(sip);
+ sdeb_meta_write_unlock(sip);
cleanup_free:
kfree(arr);
return retval;
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 04/12] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (2 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 03/12] scsi: scsi_debug: Take the zone metadata lock before the data lock Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 05/12] scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets Niklas Cassel
` (7 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
corrupt_lbas(), resp_write_dt0() and resp_write_same() call
scsi_debug_lbp() to decide whether to take the zone metadata lock, and
call it again to decide whether to touch the provisioning map that the
lock protects.
The result is not constant: scsi_debug_lbp() is false while fake_rw is
set, and fake_rw is writable at runtime, both as a module parameter and
through its driver attribute. The second call can therefore touch the
map after the first decided not to take the lock.
Call it once and use the result throughout.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 dev_size_mb=128 lbpu=1 lbpws=1
A WRITE(16) completes and GET LBA STATUS then reports the region as
mapped, which covers resp_write_dt0(). A WRITE SAME and a WRITE SAME
with the UNMAP bit set complete, and GET LBA STATUS reports the first
region as mapped and the second as deallocated, which covers all three
uses of the result in resp_write_same().
corrupt_lbas() is not covered. It is reached by writing to the corrupt
file in debugfs, and that interface rejected the requests that were
tried, for a reason that has nothing to do with this patch.
The window that the change closes was not reproduced. It needs fake_rw
to be written between two calls, and was found by review.
---
drivers/scsi/scsi_debug.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 18aefe83b7b6..b64ae3ad300d 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4953,12 +4953,11 @@ static int corrupt_lbas(struct sdebug_dev_info *devip, u64 lba, u32 num,
{
struct sdeb_store_info *sip = devip2sip(devip, false);
bool meta_data_locked = false;
+ bool lbp = scsi_debug_lbp();
u32 block, num_mapped, b, i;
int error = 0;
- if (sdebug_dev_is_zoned(devip) ||
- sdebug_dix ||
- scsi_debug_lbp()) {
+ if (sdebug_dev_is_zoned(devip) || sdebug_dix || lbp) {
sdeb_meta_write_lock(sip);
meta_data_locked = true;
}
@@ -4975,8 +4974,7 @@ static int corrupt_lbas(struct sdebug_dev_info *devip, u64 lba, u32 num,
goto out_unlock;
}
- if (scsi_debug_lbp() &&
- (!map_state(sip, lba, &num_mapped) || num > num_mapped)) {
+ if (lbp && (!map_state(sip, lba, &num_mapped) || num > num_mapped)) {
pr_err("can't modify unmapped logical blocks: %llu:%u",
lba, num);
error = -EINVAL;
@@ -5035,6 +5033,7 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
struct sdeb_store_info *sip = devip2sip(devip, true);
u8 *cmd = scp->cmnd;
bool meta_data_locked = false;
+ bool lbp = scsi_debug_lbp();
if (unlikely(sdebug_opts & SDEBUG_OPT_UNALIGNED_WRITE &&
atomic_read(&sdeb_inject_pending))) {
@@ -5101,8 +5100,7 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
}
if (sdebug_dev_is_zoned(devip) ||
- (sdebug_dix && scsi_prot_sg_count(scp)) ||
- scsi_debug_lbp()) {
+ (sdebug_dix && scsi_prot_sg_count(scp)) || lbp) {
sdeb_meta_write_lock(sip);
meta_data_locked = true;
}
@@ -5147,7 +5145,7 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
}
ret = do_device_access(sip, scp, 0, lba, num, group, true, false);
- if (unlikely(scsi_debug_lbp()))
+ if (unlikely(lbp))
map_region(sip, lba, num);
/* If ZBC zone then bump its write pointer */
@@ -5374,8 +5372,9 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
u8 *fs1p;
u8 *fsp;
bool meta_data_locked = false;
+ bool lbp = scsi_debug_lbp();
- if (sdebug_dev_is_zoned(devip) || scsi_debug_lbp()) {
+ if (sdebug_dev_is_zoned(devip) || lbp) {
sdeb_meta_write_lock(sip);
meta_data_locked = true;
}
@@ -5384,7 +5383,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
if (ret)
goto out;
- if (unmap && scsi_debug_lbp()) {
+ if (unmap && lbp) {
unmap_region(sip, lba, num);
goto out;
}
@@ -5414,7 +5413,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
block = do_div(lbaa, sdebug_store_sectors);
memmove(fsp + (block * lb_size), fs1p, lb_size);
}
- if (scsi_debug_lbp())
+ if (lbp)
map_region(sip, lba, num);
/* If ZBC zone then bump its write pointer */
if (sdebug_dev_is_zoned(devip))
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 05/12] scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (3 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 04/12] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:39 ` sashiko-bot
2026-09-28 7:21 ` [PATCH v10 06/12] scsi: scsi_debug: Report the residual of a write Niklas Cassel
` (6 subsequent siblings)
11 siblings, 1 reply; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_write_scat() computes the length of an LBA range in bytes as a
32-bit product, num_by = num * lb_size, and adds it to sg_off, the
32-bit offset of the next range in the data-out buffer.
check_device_access_params() limits num to the number of sectors in the
store, so the product wraps once the store is 4 GiB or larger, and
sg_off wraps with it. The ranges that follow are then written from the
wrong offset in the buffer.
Make num_by and sg_off 64-bit. do_device_access() takes a 32-bit
offset, so pass it sg_off capped at U32_MAX: a data-out buffer is at
most U32_MAX bytes, so a capped offset is still at or past its end and
nothing is copied.
Assisted-by: LLM
Fixes: 481b5e5c7949 ("scsi: scsi_debug: add resp_write_scat function")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 dev_size_mb=4097
in a guest with 8 GiB of memory, issuing a WRITE SCATTERED (16) through
SG_IO with two LBA range descriptors: 8388608 blocks at LBA 0, which is
exactly 4 GiB, and 8 blocks at LBA 8388608. The 8704 byte buffer holds
the parameter list and 16 blocks, so the first range consumes all of it.
Before this patch the offset wrapped, and the second range was written
with the data of the first; after it the second range is left as it
was.
---
drivers/scsi/scsi_debug.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index b64ae3ad300d..8f9d54269dce 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5197,7 +5197,8 @@ static int resp_write_scat(struct scsi_cmnd *scp,
struct sdeb_store_info *sip = devip2sip(devip, true);
u8 wrprotect;
u16 lbdof, num_lrd, k;
- u32 num, num_by, bt_len, lbdof_blen, sg_off, cum_lb;
+ u32 num, bt_len, lbdof_blen, cum_lb;
+ u64 num_by, sg_off;
u32 lb_size = sdebug_sector_size;
u32 ei_lba;
u64 lba;
@@ -5273,14 +5274,14 @@ static int resp_write_scat(struct scsi_cmnd *scp,
num = get_unaligned_be32(up + 8);
if (sdebug_verbose)
sdev_printk(KERN_INFO, scp->device,
- "%s: k=%d LBA=0x%llx num=%u sg_off=%u\n",
+ "%s: k=%d LBA=0x%llx num=%u sg_off=%llu\n",
my_name, k, lba, num, sg_off);
if (num == 0)
continue;
ret = check_device_access_params(scp, lba, num, true);
if (ret)
goto err_out_unlock;
- num_by = num * lb_size;
+ num_by = (u64)num * lb_size;
ei_lba = is_16 ? 0 : get_unaligned_be32(up + 12);
if ((cum_lb + num) > bt_len) {
@@ -5311,7 +5312,8 @@ static int resp_write_scat(struct scsi_cmnd *scp,
* Write ranges atomically to keep as close to pre-atomic
* writes behaviour as possible.
*/
- ret = do_device_access(sip, scp, sg_off, lba, num, group, true, true);
+ ret = do_device_access(sip, scp, min_t(u64, sg_off, U32_MAX), lba,
+ num, group, true, true);
/* If ZBC zone then bump its write pointer */
if (sdebug_dev_is_zoned(devip))
zbc_inc_wp(devip, lba, num);
@@ -5322,7 +5324,7 @@ static int resp_write_scat(struct scsi_cmnd *scp,
goto err_out_unlock;
} else if (unlikely(sdebug_verbose && (ret < num_by)))
sdev_printk(KERN_INFO, scp->device,
- "%s: write: cdb indicated=%u, IO sent=%d bytes\n",
+ "%s: write: cdb indicated=%llu, IO sent=%d bytes\n",
my_name, num_by, ret);
if (unlikely((sdebug_opts & SDEBUG_OPT_RECOV_DIF_DIX) &&
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 06/12] scsi: scsi_debug: Report the residual of a write
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (4 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 05/12] scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 8:18 ` Damien Le Moal
2026-09-28 7:21 ` [PATCH v10 07/12] scsi: scsi_debug: Enforce physical block alignment of zoned writes Niklas Cassel
` (5 subsequent siblings)
11 siblings, 1 reply; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
Documentation/scsi/scsi_mid_low_api.rst defines the residual as the
length of the data buffer less the number of bytes actually transferred,
so a write whose data buffer is larger than its transfer length leaves
one. resp_read_dt0() and resp_write_tape() report it, but the write
paths for a disk do not, so such a write completes with GOOD status and
a residual of zero while a READ of the same length into the same buffer
reports it correctly.
Report it in resp_write_dt0(), resp_write_scat() and resp_atomic_write().
resp_write_scat() differs twice over. Its data-out buffer also holds the
parameter list header and the LBA range descriptors, so what the command
consumed is sg_off rather than the bytes that the last range
transferred; and sg_off counts what was asked for rather than what was
transferred, so it can exceed the buffer and needs a guard. A command
with no LBA range descriptors or a buffer transfer length of zero
transfers nothing, so the whole buffer is residual. An error, real or
injected, can end the command after some ranges have been written, so
the residual is reported on every exit once the parameter list has been
fetched.
Assisted-by: LLM
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
An eight logical block WRITE(16) with a 5000 byte buffer reports
resid=904, having transferred 4096. A READ(16) of the same length into
the same buffer reported that before this patch and the WRITE reported
0. A buffer of exactly 4096 bytes reports 0, as does a 512 byte buffer,
which is consumed in its entirety. WRITE ATOMIC (16) behaves like the
WRITE, on a device that is not zoned.
WRITE SCATTERED (16) with one LBA range descriptor of eight blocks and a
5632 byte buffer reports resid=1024, having consumed 512 bytes of
parameter list and 4096 bytes of data. The same command with a 1024 byte
buffer completes and reports resid=0: sg_off reaches 4608, past the end
of the buffer, and without the guard the residual would have been
reported as 4294963712.
Changes since v9: the commit message cites the definition of the
residual in Documentation/scsi/scsi_mid_low_api.rst.
---
drivers/scsi/scsi_debug.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 8f9d54269dce..691a5ad56161 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5162,6 +5162,8 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
"%s: write: cdb indicated=%u, IO sent=%d bytes\n",
my_name, num * sdebug_sector_size, ret);
+ scsi_set_resid(scp, scsi_bufflen(scp) - ret);
+
if (unlikely((sdebug_opts & SDEBUG_OPT_RECOV_DIF_DIX) &&
atomic_read(&sdeb_inject_pending))) {
if (sdebug_opts & SDEBUG_OPT_RECOVERED_ERR) {
@@ -5234,8 +5236,10 @@ static int resp_write_scat(struct scsi_cmnd *scp,
"Unprotected WR to DIF device\n");
}
}
- if ((num_lrd == 0) || (bt_len == 0))
+ if (num_lrd == 0 || bt_len == 0) {
+ scsi_set_resid(scp, scsi_bufflen(scp));
return 0; /* T10 says these do-nothings are not errors */
+ }
if (lbdof == 0) {
if (sdebug_verbose)
sdev_printk(KERN_INFO, scp->device,
@@ -5329,6 +5333,8 @@ static int resp_write_scat(struct scsi_cmnd *scp,
if (unlikely((sdebug_opts & SDEBUG_OPT_RECOV_DIF_DIX) &&
atomic_read(&sdeb_inject_pending))) {
+ /* This range has been written */
+ sg_off += num_by;
if (sdebug_opts & SDEBUG_OPT_RECOVERED_ERR) {
mk_sense_buffer(scp, RECOVERED_ERROR,
FAILURE_PREDICTION_THRESHOLD_EXCEEDED);
@@ -5354,6 +5360,13 @@ static int resp_write_scat(struct scsi_cmnd *scp,
}
ret = 0;
err_out_unlock:
+ /*
+ * sg_off counts what the command asked for, which can exceed the
+ * buffer: lbdof is not validated against it, and a range is counted
+ * in full even if do_device_access() copied less.
+ */
+ if (scsi_bufflen(scp) > sg_off)
+ scsi_set_resid(scp, scsi_bufflen(scp) - sg_off);
sdeb_meta_write_unlock(sip);
err_out:
kfree(lrdp);
@@ -6208,6 +6221,8 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
return DID_ERROR << 16;
if (unlikely(ret != len * sdebug_sector_size))
return DID_ERROR << 16;
+
+ scsi_set_resid(scp, scsi_bufflen(scp) - ret);
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 07/12] scsi: scsi_debug: Enforce physical block alignment of zoned writes
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (5 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 06/12] scsi: scsi_debug: Report the residual of a write Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 08/12] scsi: scsi_debug: Do not write a partial physical block to a zoned device Niklas Cassel
` (4 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
ZBC-3 r06 (T10/BSR INCITS 579), 4.5.3.3.2, requires a write to a
sequential write required zone to be terminated with ILLEGAL REQUEST /
UNALIGNED WRITE COMMAND both when it does not start at the write pointer
and when it does not end on a physical block boundary. That is why
sd_zbc_read_zones() sets the zone_write_granularity queue limit to the
physical block size of a host-managed device.
check_zbc_access_params() implements the first condition but not the
second. So with zbc=managed sector_size=512 physblk_exp=3, a write of a
single logical block at the write pointer is accepted and advances the
write pointer by one logical block, leaving it off the granularity that
was advertised: nothing can write at it any more, and the zone can only
be used again after being reset.
Check the ending LBA as well, with the same sense data, as the standard
gives both conditions the same. An entire medium write same command,
which the standard excludes, needs no special case: the preceding check
already terminates it with WRITE BOUNDARY VIOLATION. Sequential write
preferred zones are left alone, as writes to them need not be
sequential, and with the default physblk_exp=0 the check is a no-op.
Assisted-by: LLM
Fixes: f0d1cf9378bd ("scsi: scsi_debug: Add ZBC zone commands")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
Before this patch, a one logical block WRITE(16) at the write pointer of
an empty sequential write required zone completes with GOOD status and
leaves the write pointer at LBA 0x18001, which is not a multiple of the
4096 byte zone_write_granularity reported for the device. After it, the
same command is terminated with ILLEGAL REQUEST / UNALIGNED WRITE
COMMAND and the zone is left EMPTY, while an aligned eight block write
is still accepted.
---
drivers/scsi/scsi_debug.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 691a5ad56161..b4583fe10ec5 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -3980,6 +3980,16 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
UNALIGNED_WRITE_COMMAND);
return check_condition_result;
}
+ /*
+ * Writes must end on a physical block boundary, that is, the
+ * transfer length must be a multiple of the physical block
+ * size.
+ */
+ if (!IS_ALIGNED(lba + num, 1U << sdebug_physblk_exp)) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST,
+ UNALIGNED_WRITE_COMMAND);
+ return check_condition_result;
+ }
}
/* Handle implicit open of closed and empty zones */
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 08/12] scsi: scsi_debug: Do not write a partial physical block to a zoned device
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (6 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 07/12] scsi: scsi_debug: Enforce physical block alignment of zoned writes Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 09/12] scsi: scsi_debug: Advance the write pointer over the data written Niklas Cassel
` (3 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
do_device_access() copies one logical block at a time and stops at the
first short copy, so a data-out buffer smaller than the transfer length
of the command can leave part of a physical block written. An initiator
can arrange that with SG_IO.
That is acceptable everywhere but a sequential write required zone,
where ZBC-3 r06 (T10/BSR INCITS 579), 4.5.3.3.2, requires a write to end
on a physical block boundary.
For such a zone, stop at the last whole physical block that the buffer
holds. The bytes left over are not written, and are reported to the
initiator as part of the residual.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
issuing a sixteen logical block WRITE(16) through SG_IO with a buffer of
twelve blocks. To a sequential write required zone it now transfers
eight blocks, one whole physical block, and reports the remaining 2048
bytes as the residual; before this patch it transferred twelve. To a
conventional zone it still transfers all twelve and reports no residual.
With physblk_exp=0 an eight block write with a buffer of one block still
transfers one block, wherever it is addressed.
---
drivers/scsi/scsi_debug.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index b4583fe10ec5..dd7132e92c27 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4273,6 +4273,8 @@ static int do_device_access(struct sdeb_store_info *sip, struct scsi_cmnd *scp,
u64 block;
enum dma_data_direction dir;
struct scsi_data_buffer *sdb = &scp->sdb;
+ struct scsi_device *sdp = scp->device;
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
u8 *fsp;
int i, total = 0;
@@ -4300,6 +4302,25 @@ static int do_device_access(struct sdeb_store_info *sip, struct scsi_cmnd *scp,
fsp = sip->storep;
+ /*
+ * A write to a sequential write required zone has to end on a physical
+ * block boundary, so if the data-out buffer does not hold all of the
+ * data that the command asks for, write up to the last whole physical
+ * block that it does hold. What is left over is reported as part of
+ * the residual.
+ */
+ if (do_write && sdebug_dev_is_zoned(devip)) {
+ struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
+
+ if (zsp->z_type == ZBC_ZTYPE_SWR) {
+ u32 avail = (sdb->length - sg_skip)
+ >> ilog2(sdebug_sector_size);
+
+ if (avail < num)
+ num = round_down(avail, 1U << sdebug_physblk_exp);
+ }
+ }
+
block = do_div(lba, sdebug_store_sectors);
/* Only allow 1x atomic write or multiple non-atomic writes at any given time */
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 09/12] scsi: scsi_debug: Advance the write pointer over the data written
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (7 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 08/12] scsi: scsi_debug: Do not write a partial physical block to a zoned device Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 10/12] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
` (2 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_write_dt0() and resp_write_scat() advance the write pointer of a
sequential write required zone by the transfer length of the command
without looking at what do_device_access() returned, which is -1 when
the data direction of the command does not match the operation, and a
short byte count when the data-out buffer is smaller than the transfer
length. An initiator can produce both with SG_IO.
The zone then describes more data than is on the medium, and a write at
the position where the data really ends is terminated with UNALIGNED
WRITE COMMAND, so the zone has to be reset before it can be written to
again.
Advance the write pointer over the data that was written instead. As
do_device_access() does not write a partial physical block to such a
zone, that leaves the write pointer where a write can end.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: f0d1cf9378bd ("scsi: scsi_debug: Add ZBC zone commands")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
issuing WRITE(16) through SG_IO with a data-out buffer shorter than the
transfer length of the command. A sixteen block write with a buffer of
twelve blocks leaves the write pointer of an empty sequential write
required zone eight blocks on, which is where the data it wrote ends,
and a following eight block write continues from there. An eight block
write with a buffer of one block writes nothing and leaves the write
pointer where it was.
Before this patch both advanced the write pointer by the full transfer
length, and a write at the end of the data that had actually been
written was then terminated with UNALIGNED WRITE COMMAND.
---
drivers/scsi/scsi_debug.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index dd7132e92c27..f83440ab7966 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5179,9 +5179,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
if (unlikely(lbp))
map_region(sip, lba, num);
- /* If ZBC zone then bump its write pointer */
- if (sdebug_dev_is_zoned(devip))
- zbc_inc_wp(devip, lba, num);
+ /* If ZBC zone then bump its write pointer over the data written */
+ if (sdebug_dev_is_zoned(devip) && ret > 0)
+ zbc_inc_wp(devip, lba, ret >> ilog2(sdebug_sector_size));
if (meta_data_locked)
sdeb_meta_write_unlock(sip);
@@ -5349,9 +5349,10 @@ static int resp_write_scat(struct scsi_cmnd *scp,
*/
ret = do_device_access(sip, scp, min_t(u64, sg_off, U32_MAX), lba,
num, group, true, true);
- /* If ZBC zone then bump its write pointer */
- if (sdebug_dev_is_zoned(devip))
- zbc_inc_wp(devip, lba, num);
+ /* If ZBC zone then bump its write pointer over the data written */
+ if (sdebug_dev_is_zoned(devip) && ret > 0)
+ zbc_inc_wp(devip, lba,
+ ret >> ilog2(sdebug_sector_size));
if (unlikely(scsi_debug_lbp()))
map_region(sip, lba, num);
if (unlikely(-1 == ret)) {
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 10/12] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (8 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 09/12] scsi: scsi_debug: Advance the write pointer over the data written Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 11/12] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 12/12] scsi: scsi_debug: Validate the access parameters of " Niklas Cassel
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
A write whose data-out buffer is shorter than its transfer length is
short. An ordinary write transfers what the buffer holds and reports the
rest as a residual, but an atomic write cannot be short: SBC-6 r02
(T10/BSR INCITS 587), 4.28.1, requires each atomic write operation to
write either all of its data or none of it, and 4.28.2 requires one that
cannot complete to leave the LBAs that it specifies unaltered.
resp_atomic_write() does fail a short WRITE ATOMIC (16) with DID_ERROR,
but only after do_device_access() has returned, and do_device_access()
copies one logical block at a time, so by then the blocks that the
buffer did hold have been written. An eight block WRITE ATOMIC (16) with
a buffer of four fails having overwritten the first four.
Check the length of the buffer before writing anything, and fail the
command with the same DID_ERROR as before.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
atomic_wr=1 lbpu=1
issuing a WRITE ATOMIC (16) of eight blocks through SG_IO with a buffer
of four blocks of 0xaa. It fails with DID_ERROR before and after this
patch, but before it the first four blocks read back as 0xaa afterwards,
and after it all eight read back as they were. A WRITE ATOMIC (16) with
a full buffer writes all eight blocks, as before.
---
drivers/scsi/scsi_debug.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index f83440ab7966..8c8b409f921a 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6248,6 +6248,10 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
}
}
+ /* Short atomic writes are not allowed. */
+ if (scsi_bufflen(scp) < len * sdebug_sector_size)
+ return DID_ERROR << 16;
+
ret = do_device_access(sip, scp, 0, lba, len, 0, true, true);
if (unlikely(ret == -1))
return DID_ERROR << 16;
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 11/12] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16)
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (9 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 10/12] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 12/12] scsi: scsi_debug: Validate the access parameters of " Niklas Cassel
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
When logical block provisioning is enabled, every command that writes
user data calls map_region() so that GET LBA STATUS reports the region
as mapped. resp_atomic_write() does not, so GET LBA STATUS keeps
reporting a region as deallocated after a WRITE ATOMIC (16) has written
it.
Call map_region() the way resp_write_dt0() does, holding the zone
metadata write lock across the access. That lock is what serialises the
map against resp_unmap(), which does nothing unless logical block
provisioning is enabled.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
atomic_wr=1 lbpu=1
GET LBA STATUS reports a region that has never been written as
deallocated, and reports it as mapped after a WRITE ATOMIC (16) of eight
blocks. Only an ordinary WRITE did so before this patch.
---
drivers/scsi/scsi_debug.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 8c8b409f921a..8fc515a7a7e1 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4876,6 +4876,11 @@ static unsigned int map_state(struct sdeb_store_info *sip, sector_t lba,
return mapped;
}
+/*
+ * Callers may map the whole range of a write without checking how much of
+ * it was written. SBC-6 4.7.4.6.2 allows a deallocated LBA to become mapped
+ * at any time, and only requires an LBA that was written to be mapped.
+ */
static void map_region(struct sdeb_store_info *sip, sector_t lba,
unsigned int len)
{
@@ -6204,6 +6209,7 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
u8 *cmd = scp->cmnd;
u16 boundary, len;
u64 lba, lba_tmp;
+ bool lbp = scsi_debug_lbp();
int ret;
if (!scsi_debug_atomic_write()) {
@@ -6252,7 +6258,15 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
if (scsi_bufflen(scp) < len * sdebug_sector_size)
return DID_ERROR << 16;
+ if (lbp)
+ sdeb_meta_write_lock(sip);
+
ret = do_device_access(sip, scp, 0, lba, len, 0, true, true);
+ if (lbp) {
+ map_region(sip, lba, len);
+ sdeb_meta_write_unlock(sip);
+ }
+
if (unlikely(ret == -1))
return DID_ERROR << 16;
if (unlikely(ret != len * sdebug_sector_size))
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v10 12/12] scsi: scsi_debug: Validate the access parameters of WRITE ATOMIC (16)
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (10 preceding siblings ...)
2026-09-28 7:21 ` [PATCH v10 11/12] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
@ 2026-09-28 7:21 ` Niklas Cassel
11 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-09-28 7:21 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_atomic_write() validates the fields that are specific to an atomic
write, but never the range that the command addresses. Every other
command that writes user data calls check_device_access_params() first,
which rejects a transfer that ends beyond the capacity of the device,
one whose length exceeds the size of the store, and any write to a write
protected device.
Call check_device_access_params(). The zone checks that it ends with are
unreachable, as atomic writes and ZBC emulation are mutually exclusive.
Assisted-by: LLM
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
atomic_wr=1
The device has a capacity of 262144 logical blocks. A WRITE ATOMIC (16)
of eight blocks at LBA 262140 is now terminated with ILLEGAL REQUEST /
LOGICAL BLOCK ADDRESS OUT OF RANGE; before this patch it completed with
GOOD status, having written eight blocks at LBA 0 instead.
With the wp module parameter set to 1, a WRITE ATOMIC (16) is now
terminated with DATA PROTECT / LOGICAL UNIT SOFTWARE WRITE PROTECTED,
which is what an ordinary WRITE(16) has always returned; before this
patch it completed with GOOD status and wrote the data.
---
drivers/scsi/scsi_debug.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 8fc515a7a7e1..f4aa4cd401e9 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6254,6 +6254,10 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
}
}
+ ret = check_device_access_params(scp, lba, len, true);
+ if (ret)
+ return ret;
+
/* Short atomic writes are not allowed. */
if (scsi_bufflen(scp) < len * sdebug_sector_size)
return DID_ERROR << 16;
--
2.55.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v10 05/12] scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets
2026-09-28 7:21 ` [PATCH v10 05/12] scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets Niklas Cassel
@ 2026-09-28 7:39 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-09-28 7:39 UTC (permalink / raw)
To: Niklas Cassel; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] scsi: scsi_debug: out-of-bounds read in sg_copy_buffer via off_t underflow
--
commit 84b5a3ba9d6d206750a298cdc52cd60cfbdeb0a6
Author: Niklas Cassel <cassel@kernel.org>
scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets
This commit changes num_by and sg_off to 64-bit to avoid 32-bit overflow
issues in resp_write_scat() when handling large LBA ranges. When calling
do_device_access(), it caps sg_off at U32_MAX.
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index b64ae3ad300d8..8f9d54269dcec 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
[ ... ]
> @@ -5311,7 +5312,8 @@ static int resp_write_scat(struct scsi_cmnd *scp,
> * Write ranges atomically to keep as close to pre-atomic
> * writes behaviour as possible.
> */
> - ret = do_device_access(sip, scp, sg_off, lba, num, group, true, true);
> + ret = do_device_access(sip, scp, min_t(u64, sg_off, U32_MAX), lba,
> + num, group, true, true);
[Severity: High]
Does capping the offset to U32_MAX here lead to an out-of-bounds memory read
on 32-bit systems when processing descriptors beyond 4 GiB?
do_device_access() takes the capped value as a u32 (sg_skip), and then
passes it down to sg_copy_buffer() which expects an off_t:
lib/scatterlist.c:sg_copy_buffer() {
size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
void *buf, size_t buflen, off_t skip,
bool to_buffer)
}
On 32-bit platforms, off_t is a signed 32-bit integer, so the unsigned
U32_MAX (0xFFFFFFFF) is implicitly converted into -1.
When sg_copy_buffer() calls sg_miter_skip(), it uses this negative skip
value:
lib/scatterlist.c:sg_miter_skip() {
...
consumed = min_t(off_t, offset, miter->__remaining);
...
}
Because offset is -1 and off_t is signed, min_t() selects -1 as the
consumed amount. This bypasses the skip loop and offsets the iterator
backwards, setting its internal byte offset to 0xFFFFFFFF.
Could this cause subsequent pointer arithmetic to point one byte before the
mapped page, resulting in an out-of-bounds memory copy?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260928072102.725566-14-cassel@kernel.org?part=5
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v10 06/12] scsi: scsi_debug: Report the residual of a write
2026-09-28 7:21 ` [PATCH v10 06/12] scsi: scsi_debug: Report the residual of a write Niklas Cassel
@ 2026-09-28 8:18 ` Damien Le Moal
0 siblings, 0 replies; 15+ messages in thread
From: Damien Le Moal @ 2026-09-28 8:18 UTC (permalink / raw)
To: Niklas Cassel, James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, John Garry
On 2026/09/28 9:21, Niklas Cassel wrote:
> Documentation/scsi/scsi_mid_low_api.rst defines the residual as the
> length of the data buffer less the number of bytes actually transferred,
> so a write whose data buffer is larger than its transfer length leaves
> one. resp_read_dt0() and resp_write_tape() report it, but the write
> paths for a disk do not, so such a write completes with GOOD status and
> a residual of zero while a READ of the same length into the same buffer
> reports it correctly.
>
> Report it in resp_write_dt0(), resp_write_scat() and resp_atomic_write().
>
> resp_write_scat() differs twice over. Its data-out buffer also holds the
> parameter list header and the LBA range descriptors, so what the command
> consumed is sg_off rather than the bytes that the last range
> transferred; and sg_off counts what was asked for rather than what was
> transferred, so it can exceed the buffer and needs a guard. A command
> with no LBA range descriptors or a buffer transfer length of zero
> transfers nothing, so the whole buffer is residual. An error, real or
> injected, can end the command after some ranges have been written, so
> the residual is reported on every exit once the parameter list has been
> fetched.
>
> Assisted-by: LLM
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
> ---
> Tested with:
>
> modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
> zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
>
> An eight logical block WRITE(16) with a 5000 byte buffer reports
> resid=904, having transferred 4096. A READ(16) of the same length into
> the same buffer reported that before this patch and the WRITE reported
> 0. A buffer of exactly 4096 bytes reports 0, as does a 512 byte buffer,
> which is consumed in its entirety. WRITE ATOMIC (16) behaves like the
> WRITE, on a device that is not zoned.
>
> WRITE SCATTERED (16) with one LBA range descriptor of eight blocks and a
> 5632 byte buffer reports resid=1024, having consumed 512 bytes of
> parameter list and 4096 bytes of data. The same command with a 1024 byte
> buffer completes and reports resid=0: sg_off reaches 4608, past the end
> of the buffer, and without the guard the residual would have been
> reported as 4294963712.
>
> Changes since v9: the commit message cites the definition of the
> residual in Documentation/scsi/scsi_mid_low_api.rst.
> ---
> drivers/scsi/scsi_debug.c | 17 ++++++++++++++++-
> 1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 8f9d54269dce..691a5ad56161 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -5162,6 +5162,8 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
> "%s: write: cdb indicated=%u, IO sent=%d bytes\n",
> my_name, num * sdebug_sector_size, ret);
>
> + scsi_set_resid(scp, scsi_bufflen(scp) - ret);
May be do this under a if:
if (ret < scsi_bufflen(scp))
scsi_set_resid(scp, scsi_bufflen(scp) - ret);
> @@ -6208,6 +6221,8 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
> return DID_ERROR << 16;
> if (unlikely(ret != len * sdebug_sector_size))
> return DID_ERROR << 16;
> +
> + scsi_set_resid(scp, scsi_bufflen(scp) - ret);
And here too.
> return 0;
> }
With that,
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-09-28 8:18 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28 7:21 [PATCH v10 00/12] scsi: scsi_debug: fix zoned write validation Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 01/12] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 02/12] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 03/12] scsi: scsi_debug: Take the zone metadata lock before the data lock Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 04/12] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 05/12] scsi: scsi_debug: Avoid 32-bit overflow in WRITE SCATTERED offsets Niklas Cassel
2026-09-28 7:39 ` sashiko-bot
2026-09-28 7:21 ` [PATCH v10 06/12] scsi: scsi_debug: Report the residual of a write Niklas Cassel
2026-09-28 8:18 ` Damien Le Moal
2026-09-28 7:21 ` [PATCH v10 07/12] scsi: scsi_debug: Enforce physical block alignment of zoned writes Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 08/12] scsi: scsi_debug: Do not write a partial physical block to a zoned device Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 09/12] scsi: scsi_debug: Advance the write pointer over the data written Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 10/12] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 11/12] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
2026-09-28 7:21 ` [PATCH v10 12/12] scsi: scsi_debug: Validate the access parameters of " Niklas Cassel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox