From 93eb067befefd3f6d3b5adec9b094dfcc9dcf9dc Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 19 Nov 2013 05:51:39 +0000 Subject: [PATCH] scst: Avoid that scst_write_same() crashes for large data len A quote from SBC-3 about WRITE SAME (10): "If the number of logical blocks specified to be unmapped or written exceeds the value indicated in the MAXIMUM WRITE SAME LENGTH field in the Block Limits VPD page (see 6.6.3), then the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to INVALID FIELD IN CDB." This patch fixes the following crash since with the current implementation data_len >= 2**63 makes (unsigned)ws_sg_cnt >= 2**31 and this causes sg_init_table() to crash as follows: BUG: unable to handle kernel paging request at 00000027ffffffe8 IP: [] sg_init_table+0x5e/0x90 CPU: 1 PID: 10297 Comm: disk014_1 Tainted: G O 3.11.0-debug+ #1 RIP: 0010:[] [] sg_init_table+0x5e/0x90 Call Trace: [] scst_write_same+0x236/0x340 [scst] [] ? vdisk_exec_write_same_unmap.isra.35+0x170/0x170 [scst_vdisk] [] vdisk_exec_write_same+0x74/0x170 [scst_vdisk] [] vdev_do_job+0x15e/0x390 [scst_vdisk] [] vdisk_exec+0x25/0x70 [scst_vdisk] [] scst_do_real_exec+0xa5/0x3d0 [scst] [] scst_exec_check_blocking+0xea/0x310 [scst] [] scst_exec_check_sn+0x195/0x2f0 [scst] [] scst_process_active_cmd+0x521/0x750 [scst] [] scst_do_job_active+0x81/0x1a0 [scst] [] scst_cmd_thread+0x157/0x340 [scst] [] kthread+0xd6/0xe0 [] ret_from_fork+0x7c/0xb0 Signed-off-by: Bart Van Assche git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5122 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/include/scst.h | 3 +++ scst/src/dev_handlers/scst_vdisk.c | 5 +++-- scst/src/scst_lib.c | 10 +++++++++- scst/src/scst_main.c | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/scst/include/scst.h b/scst/include/scst.h index 31ac605ee..47162e3ff 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2501,6 +2501,9 @@ struct scst_device { /* List of blocked commands, protected by dev_lock. */ struct list_head blocked_cmd_list; + /* MAXIMUM WRITE SAME LENGTH in bytes */ + uint64_t max_write_same_len; + /* A list entry used during TM */ struct list_head tm_dev_list_entry; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index be0372a20..20fcba6d5 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -2558,8 +2558,9 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) } } - /* MAXIMUM WRITE SAME LENGTH (512MB) */ - put_unaligned_be64((512*1024*1024) >> dev->block_shift, &buf[36]); + /* MAXIMUM WRITE SAME LENGTH (measured in blocks) */ + put_unaligned_be64(dev->max_write_same_len >> + dev->block_shift, &buf[36]); resp_len = buf[3] + 4; } else if ((0xB1 == cmd->cdb[2]) && (dev->type == TYPE_DISK)) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 5a9bce286..4a698bd5f 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -4870,7 +4870,7 @@ void scst_write_same(struct scst_cmd *cmd) } if (cmd->sg_cnt != 1) { - PRINT_ERROR("WRITE SAME must contain only single block of data " + PRINT_WARNING("WRITE SAME must contain only single block of data " "in a single SG (cmd %p)", cmd); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_parameter_value_invalid)); goto out_done; @@ -4881,6 +4881,14 @@ void scst_write_same(struct scst_cmd *cmd) goto out_done; } + if (unlikely((uint64_t)cmd->data_len > cmd->dev->max_write_same_len)) { + PRINT_WARNING("Invalid WRITE SAME data len %lld (max allowed " + "%lld)", (long long)cmd->data_len, + (long long)cmd->dev->max_write_same_len); + scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0); + goto out_done; + } + wsp = kzalloc(sizeof(*wsp), GFP_KERNEL); if (wsp == NULL) { PRINT_ERROR("Unable to allocate ws_priv (size %zd, cmd %p)", diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index bdd27987e..a6bf13269 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -2080,6 +2080,7 @@ assign: dev->threads_num = handler->threads_num; dev->threads_pool_type = handler->threads_pool_type; + dev->max_write_same_len = 512 * 1024 * 1024; /* 512 MB */ if (handler->attach) { TRACE_DBG("Calling new dev handler's attach(%p)", dev);