scst_lib: Reject invalid block lengths in READ CAPACITY responses

scst_calc_block_shift() warns when the sector size is not a power of two
but still returns its floor log2. When a backend returns a malformed READ
CAPACITY response, scst_block_generic_dev_done() therefore changes the
device geometry to an unrelated block size. For example, a block length
of 809052976 produces

  1 << 29 != 809052976
  WARNING: ... at scst_calc_block_shift+0x9c/0xe0 [scst]

and the device is switched to 536870912-byte blocks. Subsequent READ and
WRITE commands then fail in PARSE until another READ CAPACITY restores the
original block size. A zero block length is only logged, and the
response is still returned to the initiator with GOOD status.

Treat sector sizes below 512 bytes or not a power of two as invalid in
scst_calc_block_shift() instead of warning. When a READ CAPACITY response
reports a zero or invalid block length, keep the current geometry and
complete the command with INTERNAL TARGET FAILURE sense, so that the
initiator does not use the malformed capacity data.
This commit is contained in:
wenlxie
2026-10-18 13:12:41 +03:00
committed by Gleb Chesnokov
parent 9917205344
commit eebc66e8a8
+12 -12
View File
@@ -12444,13 +12444,11 @@ int scst_calc_block_shift(int sector_size)
if (sector_size == 0)
sector_size = 512;
block_shift = ilog2(sector_size);
WARN_ONCE(1 << block_shift != sector_size, "1 << %d != %d\n",
block_shift, sector_size);
if (block_shift < 9) {
if (sector_size < 512 || !is_power_of_2(sector_size)) {
PRINT_ERROR("Wrong sector size %d", sector_size);
block_shift = -1;
} else {
block_shift = ilog2(sector_size);
}
TRACE_EXIT_RES(block_shift);
@@ -12834,14 +12832,16 @@ int scst_block_generic_dev_done(struct scst_cmd *cmd,
}
sector_size = get_unaligned_be32(&buffer[sect_sz_off]);
scst_put_buf_full(cmd, buffer);
if (sector_size != 0) {
sh = scst_calc_block_shift(sector_size);
set_block_shift(cmd, sh);
TRACE_DBG("block_shift %d", sh);
} else {
PRINT_ERROR("Sector size in %s response is 0",
cmd->op_name);
sh = sector_size ? scst_calc_block_shift(sector_size) : -1;
if (sh < 0) {
PRINT_ERROR("Invalid sector size %u in %s response",
sector_size, cmd->op_name);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_internal_failure));
goto out;
}
set_block_shift(cmd, sh);
TRACE_DBG("block_shift %d", sh);
}
}