Re: [PATCH v2] scsi: scsi_debug: fix one-partition tape setup bounds
From: Samuel Moelius
Date: Thu Jun 04 2026 - 14:39:12 EST
On Thu, Jun 4, 2026 at 9:38 AM James Bottomley
<James.Bottomley@xxxxxxxxxxxxxxxxxxxxx> wrote:
>
> On Wed, 2026-06-03 at 23:55 +0000, Samuel Moelius wrote:
> > The tape setup path writes partition metadata one element past the
> > allocated tape_blocks array when a one-partition configuration is
> > selected.
> >
> > That corrupts adjacent state during device initialization before any
> > command is issued.
>
> I still don't get what the actual problem is. For a single partition
> tape I can't see where scsi_debug would actually do anything with
> tape_blocks[1]. What is it that you're seeing when using scsi_debug
> that motivates this?
The bug is a kernel OOB write. I can share a PoC if desired. The PoC
sends this SCSI command through /dev/sgN:
unsigned char format_medium[6] = { 0x04, 0, 0, 0, 0, 0 };
rc = sg_cmd(fd, "format_medium_one_partition",
format_medium, sizeof(format_medium));
Inside sg_cmd(), that becomes an SG_IO ioctl:
hdr.interface_id = 'S';
hdr.dxfer_direction = SG_DXFER_NONE;
hdr.cmd_len = cdb_len;
hdr.cmdp = cdb;
hdr.timeout = 10000;
ioctl(fd, SG_IO, &hdr);
For scsi_debug tape devices, opcode 0x04 dispatches here:
{ 0, 0x4, 0, DS_SSC, 0, resp_format_medium, NULL,
/* FORMAT MEDIUM (6) */ }
resp_format_medium() sees cmd[2] == 0, meaning the default
one-partition format path:
if (cmd[2] != 0) {
...
} else {
res = partition_tape(devip, 1, TAPE_UNITS, 0);
}
So partition_tape() is called with:
nbr_partitions = 1
part_0_size = TAPE_UNITS = 10000
part_1_size = 0
The unpatched code only checked total size:
if (part_0_size + part_1_size > TAPE_UNITS)
return -1;
That passes:
10000 + 0 == 10000
Then it initializes partition 0:
devip->tape_eop[0] = part_0_size;
devip->tape_blocks[0]->fl_size = TAPE_BLOCK_EOD_FLAG;
Then the bug: it initializes partition 1 even though there is only one
partition:
devip->tape_eop[1] = part_1_size;
devip->tape_blocks[1] = devip->tape_blocks[0] +
devip->tape_eop[0];
devip->tape_blocks[1]->fl_size = TAPE_BLOCK_EOD_FLAG;
Because devip->tape_eop[0] == 10000, this computes:
devip->tape_blocks[1] = devip->tape_blocks[0] + 10000
But the allocation has only 10000 elements. So this write is one
element past the allocation.