diff options
author | Jan Engelhardt <jengelh@inai.de> | 2015-11-23 17:46:32 +0100 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-11-28 21:22:56 -0800 |
commit | d94e5a61357a04938ce14d6033b4d33a3c5fd780 (patch) | |
tree | f372bfba1269fecf0beb2d529dd93dcbf5cd63eb | |
parent | 3786dc454fa4c10b129acc0535705f1c5a64fc72 (diff) | |
download | op-kernel-dev-d94e5a61357a04938ce14d6033b4d33a3c5fd780.zip op-kernel-dev-d94e5a61357a04938ce14d6033b4d33a3c5fd780.tar.gz |
target: fix COMPARE_AND_WRITE non zero SGL offset data corruption
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>
-rw-r--r-- | drivers/target/target_core_sbc.c | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index ae24d0f..98698d8 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -561,11 +561,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++; |