]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
target: Fix possible integer underflow in UNMAP emulation
authorRoland Dreier <roland@purestorage.com>
Mon, 16 Jul 2012 22:34:24 +0000 (15:34 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 15 Aug 2012 15:10:32 +0000 (08:10 -0700)
commit b7fc7f3777582dea85156a821d78a522a0c083aa upstream.

It's possible for an initiator to send us an UNMAP command with a
descriptor that is less than 8 bytes; in that case it's really bad for
us to set an unsigned int to that value, subtract 8 from it, and then
use that as a limit for our loop (since the value will wrap around to
a huge positive value).

Fix this by making size be signed and only looping if size >= 16 (ie
if we have at least a full descriptor available).

Also remove offset as an obfuscated name for the constant 8.

Signed-off-by: Roland Dreier <roland@purestorage.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
[bwh: Backported to 3.2: adjust filename, context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/target/target_core_cdb.c

index 2150d2af3dca083d2c33405d4a6fe37739b77c0b..94d89f2b97d2a7374a5340a6613fd329b8f760e1 100644 (file)
@@ -1023,9 +1023,10 @@ int target_emulate_unmap(struct se_task *task)
        struct se_device *dev = cmd->se_dev;
        unsigned char *buf, *ptr = NULL;
        sector_t lba;
        struct se_device *dev = cmd->se_dev;
        unsigned char *buf, *ptr = NULL;
        sector_t lba;
-       unsigned int size = cmd->data_length, range;
-       int ret = 0, offset;
-       unsigned short dl, bd_dl;
+       int size = cmd->data_length;
+       u32 range;
+       int ret = 0;
+       int dl, bd_dl;
 
        if (!dev->transport->do_discard) {
                pr_err("UNMAP emulation not supported for: %s\n",
 
        if (!dev->transport->do_discard) {
                pr_err("UNMAP emulation not supported for: %s\n",
@@ -1034,20 +1035,19 @@ int target_emulate_unmap(struct se_task *task)
                return -ENOSYS;
        }
 
                return -ENOSYS;
        }
 
-       /* First UNMAP block descriptor starts at 8 byte offset */
-       offset = 8;
-       size -= 8;
-
        buf = transport_kmap_data_sg(cmd);
 
        dl = get_unaligned_be16(&buf[0]);
        bd_dl = get_unaligned_be16(&buf[2]);
 
        buf = transport_kmap_data_sg(cmd);
 
        dl = get_unaligned_be16(&buf[0]);
        bd_dl = get_unaligned_be16(&buf[2]);
 
-       ptr = &buf[offset];
-       pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu"
+       size = min(size - 8, bd_dl);
+
+       /* First UNMAP block descriptor starts at 8 byte offset */
+       ptr = &buf[8];
+       pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
                " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
 
                " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
 
-       while (size) {
+       while (size >= 16) {
                lba = get_unaligned_be64(&ptr[0]);
                range = get_unaligned_be32(&ptr[8]);
                pr_debug("UNMAP: Using lba: %llu and range: %u\n",
                lba = get_unaligned_be64(&ptr[0]);
                range = get_unaligned_be32(&ptr[8]);
                pr_debug("UNMAP: Using lba: %llu and range: %u\n",