[RHEL7,COMMIT] ms/target: fix COMPARE_AND_WRITE non zero SGL offset data corruption

Submitted by Konstantin Khorenko on April 2, 2018, 2:28 p.m.

Details

Message ID 201804021428.w32ESNBa024077@finist_ce7.work
State New
Series "target: backport bug fixes from the upstream kernel"
Headers show

Commit Message

Konstantin Khorenko April 2, 2018, 2:28 p.m.
The commit is pushed to "branch-rh7-3.10.0-693.21.1.vz7.46.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-693.21.1.vz7.46.2
------>
commit 6ee7a5696a9b835cc68b0468cf15382f98639321
Author: Jan Engelhardt <jengelh@inai.de>
Date:   Mon Apr 2 17:28:23 2018 +0300

    ms/target: fix COMPARE_AND_WRITE non zero SGL offset data corruption
    
    ML: d94e5a61357a04938ce14d6033b4d33a3c5fd780
    
    target_core_sbc's compare_and_write functionality suffers from taking
    data at the wrong memory location when writing a CAW request to disk
    when a SGL offset is non-zero.
    
    This can happen with loopback and vhost-scsi fabric drivers when
    SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is used to map existing user-space
    SGL memory into COMPARE_AND_WRITE READ/WRITE payload buffers.
    
    Given the following sample LIO subtopology,
    
    % targetcli ls /loopback/
    o- loopback ................................. [1 Target]
      o- naa.6001405ebb8df14a ....... [naa.60014059143ed2b3]
        o- luns ................................... [2 LUNs]
          o- lun0 ................ [iblock/ram0 (/dev/ram0)]
          o- lun1 ................ [iblock/ram1 (/dev/ram1)]
    % lsscsi -g
    [3:0:1:0]    disk    LIO-ORG  IBLOCK           4.0   /dev/sdc   /dev/sg3
    [3:0:1:1]    disk    LIO-ORG  IBLOCK           4.0   /dev/sdd   /dev/sg4
    
    the following bug can be observed in Linux 4.3 and 4.4~rc1:
    
    % perl -e 'print chr$_ for 0..255,reverse 0..255' >rand
    % perl -e 'print "\0" x 512' >zero
    % cat rand >/dev/sdd
    % sg_compare_and_write -i rand -D zero --lba 0 /dev/sdd
    % sg_compare_and_write -i zero -D rand --lba 0 /dev/sdd
    Miscompare reported
    % hexdump -Cn 512 /dev/sdd
    00000000  0f 0e 0d 0c 0b 0a 09 08  07 06 05 04 03 02 01 00
    00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
    *
    00000200
    
    Rather than writing all-zeroes as instructed with the -D file, it
    corrupts the data in the sector by splicing some of the original
    bytes in. The page of the first entry of cmd->t_data_sg includes the
    CDB, and sg->offset is set to a position past the CDB. I presume that
    sg->offset is also the right choice to use for subsequent sglist
    members.
    
    Signed-off-by: Jan Engelhardt <jengelh@netitwork.de>
    Tested-by: Douglas Gilbert <dgilbert@interlog.com>
    Cc: <stable@vger.kernel.org> # v3.12+
    Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
    Signed-off-by: Andrei Vagin <avagin@openvz.org>
---
 drivers/target/target_core_sbc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 17889bd68a78..1448dce5fde8 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -503,11 +503,11 @@  static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
 
 		if (block_size < PAGE_SIZE) {
 			sg_set_page(&write_sg[i], m.page, block_size,
-				    block_size);
+				    m.piter.sg->offset + block_size);
 		} else {
 			sg_miter_next(&m);
 			sg_set_page(&write_sg[i], m.page, block_size,
-				    0);
+				    m.piter.sg->offset);
 		}
 		len -= block_size;
 		i++;