From 606af434444b46a99ccc9e940f602d3485643111 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 12 Jun 2013 04:33:45 +0000 Subject: [PATCH] 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:[] [] vdisk_parse_offset+0x3b6/0x490 [scst_vdisk] Call Trace: [] fileio_alloc_and_parse+0x69/0x130 [scst_vdisk] [] vdisk_parse+0x3b/0xc0 [scst_vdisk] [] scst_parse_cmd+0x8e/0xd00 [scst] [] ? spin_unlock_irqrestore.constprop.0+0x13/0x20 [scst] [] ? debug_print_with_prefix+0x165/0x1f0 [scst] [] scst_process_active_cmd+0x425/0x760 [scst] [] scst_cmd_init_done+0x2ea/0x5c0 [scst] [] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst] [] scsi_cmnd_start+0x1f4/0x550 [iscsi_scst] [] cmnd_rx_start+0x148/0x1a0 [iscsi_scst] [] process_read_io+0x3c8/0x7f0 [iscsi_scst] [] ? local_bh_enable_ip+0x89/0xf0 [] scst_do_job_rd+0xc4/0x220 [iscsi_scst] [] istrd+0x165/0x2e0 [iscsi_scst] [] ? wake_up_bit+0x40/0x40 [] ? iscsi_task_mgmt_affected_cmds_done+0x240/0x240 [iscsi_scst] [] kthread+0xdb/0xe0 [] ? kthread_create_on_node+0x120/0x120 [] ret_from_fork+0x7c/0xb0 [] ? 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 --- scst/src/scst_lib.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 204257829..93d6477e7 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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);