scst_vdisk: Avoid triggering BUG() for large LBA's

The LBA field in READ(16) and READ(32) commands is 64 bits wide.
Hence it is possible for an initiator not only to specify an offset
that exceeds 2**63. Report an error to the initiator for such large
offsets instead of triggering a BUG().  This patch fixes the following bug:

BUG at scst/src/dev_handlers/scst_vdisk.c:1273 ((loff < 0) || __builtin_expect(!!(data_len < 0), 0))
------------[ cut here ]------------
kernel BUG at /home/bart/software/scst.git/scst/src/dev_handlers/scst_vdisk.c:1273!
invalid opcode: 0000 [#1] SMP
CPU: 0 PID: 2852 Comm: iscsird3 Tainted: G           O 3.10.0-rc1-debug+ #1
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2007
RIP: 0010:[<ffffffffa0309e76>]  [<ffffffffa0309e76>] vdisk_parse_offset+0x3b6/0x490 [scst_vdisk]
Call Trace:
 [<ffffffffa030dec9>] fileio_alloc_and_parse+0x69/0x130 [scst_vdisk]
 [<ffffffffa030e08b>] vdisk_parse+0x3b/0xc0 [scst_vdisk]
 [<ffffffffa0264a2e>] scst_parse_cmd+0x8e/0xd00 [scst]
 [<ffffffffa029b953>] ? spin_unlock_irqrestore.constprop.0+0x13/0x20 [scst]
 [<ffffffffa029bad5>] ? debug_print_with_prefix+0x165/0x1f0 [scst]
 [<ffffffffa026c2a5>] scst_process_active_cmd+0x425/0x760 [scst]
 [<ffffffffa026dc7a>] scst_cmd_init_done+0x2ea/0x5c0 [scst]
 [<ffffffffa032aaa2>] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst]
 [<ffffffffa03317a4>] scsi_cmnd_start+0x1f4/0x550 [iscsi_scst]
 [<ffffffffa0332298>] cmnd_rx_start+0x148/0x1a0 [iscsi_scst]
 [<ffffffffa03353a8>] process_read_io+0x3c8/0x7f0 [iscsi_scst]
 [<ffffffff81049929>] ? local_bh_enable_ip+0x89/0xf0
 [<ffffffffa0335894>] scst_do_job_rd+0xc4/0x220 [iscsi_scst]
 [<ffffffffa0335e65>] istrd+0x165/0x2e0 [iscsi_scst]
 [<ffffffff8106da50>] ? wake_up_bit+0x40/0x40
 [<ffffffffa0335d00>] ? iscsi_task_mgmt_affected_cmds_done+0x240/0x240 [iscsi_scst]
 [<ffffffff8106d34b>] kthread+0xdb/0xe0
 [<ffffffff8106d270>] ? kthread_create_on_node+0x120/0x120
 [<ffffffff815357ac>] ret_from_fork+0x7c/0xb0
 [<ffffffff8106d270>] ? kthread_create_on_node+0x120/0x120
---[ end trace c21ad13002ef1548 ]---
---
 scst/src/scst_lib.c |   28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)




git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4903 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2013-06-12 04:33:45 +00:00
parent 489b7eea88
commit 606af43444
+27 -3
View File
@@ -6976,12 +6976,25 @@ int scst_calc_block_shift(int sector_size)
}
EXPORT_SYMBOL_GPL(scst_calc_block_shift);
/*
* Test whether the result of a shift-left operation would be larger than
* what fits in a variable with the type of @a.
*/
#define shift_left_overflows(a, b) \
({ \
typeof (a) _minus_one = -1LL; \
bool _a_is_signed = _minus_one < 0; \
int _shift = sizeof(1ULL) * 8 - ((b) + _a_is_signed); \
_shift < 0 || ((a) & ~((1ULL << _shift) - 1)) != 0; \
})
/**
* scst_generic_parse() - Generic parse() for devices supporting an LBA
*/
static inline int scst_generic_parse(struct scst_cmd *cmd, const int timeout[3])
{
int res = 0;
const int block_shift = cmd->dev->block_shift;
int res = -EINVAL;
TRACE_ENTRY();
@@ -6991,7 +7004,6 @@ static inline int scst_generic_parse(struct scst_cmd *cmd, const int timeout[3])
*/
if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED) {
int block_shift = cmd->dev->block_shift;
/*
* No need for locks here, since *_detach() can not be
* called, when there are existing commands.
@@ -7001,8 +7013,20 @@ static inline int scst_generic_parse(struct scst_cmd *cmd, const int timeout[3])
cmd->out_bufflen = cmd->out_bufflen << block_shift;
}
cmd->timeout = timeout[cmd->op_flags & SCST_BOTH_TIMEOUTS];
if (unlikely(!(cmd->op_flags & SCST_LBA_NOT_VALID) &&
shift_left_overflows(cmd->lba, block_shift))) {
PRINT_WARNING("offset %llu * %u >= 2**63 for device %s (len %lld)",
cmd->lba, 1 << block_shift, cmd->dev->virt_name,
cmd->data_len);
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
scst_sense_block_out_range_error));
goto out;
}
cmd->timeout = timeout[cmd->op_flags & SCST_BOTH_TIMEOUTS];
res = 0;
out:
TRACE_DBG("res %d, bufflen %d, data_len %lld, direct %d", res,
cmd->bufflen, (long long)cmd->data_len, cmd->data_direction);