From 9f3c9df5260da5e8a21b3e603328ebb6eaafc0d7 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Thu, 24 Apr 2014 02:30:52 +0000 Subject: [PATCH] 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: [] sg_init_table+0x5f/0x90 Call Trace: [] sgv_pool_alloc+0x3b8/0xbf0 [scst] [] scst_alloc_space+0xb6/0x290 [scst] [] scst_prepare_space+0x3b8/0x6e0 [scst] [] scst_process_active_cmd+0x455/0x7e0 [scst] [] scst_cmd_init_done+0x2f2/0x5c0 [scst] [] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst] [] scsi_cmnd_start+0x25a/0x550 [iscsi_scst] [] cmnd_rx_start+0x148/0x1a0 [iscsi_scst] [] process_read_io+0x3b8/0x800 [iscsi_scst] [] scst_do_job_rd+0xc7/0x220 [iscsi_scst] [] istrd+0x16d/0x2e0 [iscsi_scst] [] kthread+0xed/0x110 [] 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 git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5464 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/scst_lib.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 76928c722..18a61a886 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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;