scst_lib: Avoid integer overflows

This patch fixes the following kernel oops:

[3696]: scst: scst_parse_cmd:826:Warning: expected transfer length 522240 for opcode 0x08 (handler vcdrom, target iscsi) doesn't match decoded value -2048
[3696]: scst_parse_cmd:828:Suspicious CDB:
 (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F
   0: 08 1f ff ff ff 00   ......
BUG: unable to handle kernel paging request at ffff88283597f0c8
IP: [<ffffffff8135c3af>] sg_init_table+0x5f/0x90
Call Trace:
 [<ffffffffa021eb48>] sgv_pool_alloc+0x3b8/0xbf0 [scst]
 [<ffffffffa0204c16>] scst_alloc_space+0xb6/0x290 [scst]
 [<ffffffffa01e2d28>] scst_prepare_space+0x3b8/0x6e0 [scst]
 [<ffffffffa01ef4a5>] scst_process_active_cmd+0x455/0x7e0 [scst]
 [<ffffffffa01efb22>] scst_cmd_init_done+0x2f2/0x5c0 [scst]
 [<ffffffffa0156ac2>] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst]
 [<ffffffffa015d9fa>] scsi_cmnd_start+0x25a/0x550 [iscsi_scst]
 [<ffffffffa015e4b8>] cmnd_rx_start+0x148/0x1a0 [iscsi_scst]
 [<ffffffffa0161598>] process_read_io+0x3b8/0x800 [iscsi_scst]
 [<ffffffffa0161aa7>] scst_do_job_rd+0xc7/0x220 [iscsi_scst]
 [<ffffffffa016208d>] istrd+0x16d/0x2e0 [iscsi_scst]
 [<ffffffff81075bad>] kthread+0xed/0x110
 [<ffffffff816dd5bc>] ret_from_fork+0x7c/0xb0

and causes the following message to be reported instead:

[11269]: scst: scst_generic_parse:7402:***WARNING***: bufflen 16777215, data_len 16777215 or out_bufflen 0 too large for device disk12 (block size 2048)
scst_generic_parse:CDB:
 (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F
   0: 08 1f ff ff ff 00   ......

Signed-off-by: Bart Van Assche <bvanassche@acm.org>



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5464 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2014-04-24 02:30:52 +00:00
parent 77d9940242
commit 9f3c9df526

View File

@@ -7343,9 +7343,10 @@ EXPORT_SYMBOL_GPL(scst_calc_block_shift);
#define shift_left_overflows(a, b) \
({ \
typeof(a) _minus_one = -1LL; \
typeof(a) _plus_one = 1; \
bool _a_is_signed = _minus_one < 0; \
int _shift = sizeof(1ULL) * 8 - ((b) + _a_is_signed); \
_shift < 0 || ((a) & ~((1ULL << _shift) - 1)) != 0; \
int _shift = sizeof(a) * 8 - ((b) + _a_is_signed); \
_shift < 0 || ((a) & ~((_plus_one << _shift) - 1)) != 0;\
})
/**
@@ -7370,6 +7371,20 @@ static inline int scst_generic_parse(struct scst_cmd *cmd, const int timeout[3])
* No need for locks here, since *_detach() can not be
* called, when there are existing commands.
*/
bool overflow = shift_left_overflows(cmd->bufflen, block_shift) ||
shift_left_overflows(cmd->data_len, block_shift) ||
shift_left_overflows(cmd->out_bufflen, block_shift);
if (unlikely(overflow)) {
PRINT_WARNING("bufflen %u, data_len %llu or out_bufflen"
" %u too large for device %s (block size"
" %u)", cmd->bufflen, cmd->data_len,
cmd->out_bufflen, cmd->dev->virt_name,
1 << block_shift);
PRINT_BUFFER("CDB", cmd->cdb, cmd->cdb_len);
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
scst_sense_block_out_range_error));
goto out;
}
cmd->bufflen = cmd->bufflen << block_shift;
cmd->data_len = cmd->data_len << block_shift;
cmd->out_bufflen = cmd->out_bufflen << block_shift;