diff options
author | Roland Dreier <roland@purestorage.com> | 2012-07-16 15:34:24 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-08-15 08:10:32 -0700 |
commit | c329e96ae86fb69c30287a434426f2083fb2430b (patch) | |
tree | 11fc728c818baa573f3a15ee5e0bfa8df64b74c2 /drivers/target | |
parent | 6fb6469c601a80a0c883ec0be189ecf1677ecf56 (diff) |
target: Fix possible integer underflow in UNMAP emulation
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>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_cdb.c | 20 |
1 files changed, 10 insertions, 10 deletions
diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 2150d2af3dc..94d89f2b97d 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -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; - 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", @@ -1034,20 +1035,19 @@ int target_emulate_unmap(struct se_task *task) 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]); - 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); - 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", |