diff options
author | Roland Dreier <roland@purestorage.com> | 2012-07-16 18:34:24 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-07-16 20:35:36 -0400 |
commit | b7fc7f3777582dea85156a821d78a522a0c083aa (patch) | |
tree | fa190792d308e5fa42728d0d805e3338e1279ea0 /drivers/target/target_core_iblock.c | |
parent | 1a5fa4576ec8a462313c7516b31d7453481ddbe8 (diff) |
target: Fix possible integer underflow in UNMAP emulation
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>
Cc: stable@vger.kernel.org
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target/target_core_iblock.c')
-rw-r--r-- | drivers/target/target_core_iblock.c | 20 |
1 files changed, 10 insertions, 10 deletions
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index e6d08ee3166e..2efd70ca0b1d 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c | |||
@@ -325,24 +325,24 @@ static int iblock_execute_unmap(struct se_cmd *cmd) | |||
325 | struct iblock_dev *ibd = dev->dev_ptr; | 325 | struct iblock_dev *ibd = dev->dev_ptr; |
326 | unsigned char *buf, *ptr = NULL; | 326 | unsigned char *buf, *ptr = NULL; |
327 | sector_t lba; | 327 | sector_t lba; |
328 | unsigned int size = cmd->data_length, range; | 328 | int size = cmd->data_length; |
329 | int ret = 0, offset; | 329 | u32 range; |
330 | unsigned short dl, bd_dl; | 330 | int ret = 0; |
331 | 331 | int dl, bd_dl; | |
332 | /* First UNMAP block descriptor starts at 8 byte offset */ | ||
333 | offset = 8; | ||
334 | size -= 8; | ||
335 | 332 | ||
336 | buf = transport_kmap_data_sg(cmd); | 333 | buf = transport_kmap_data_sg(cmd); |
337 | 334 | ||
338 | dl = get_unaligned_be16(&buf[0]); | 335 | dl = get_unaligned_be16(&buf[0]); |
339 | bd_dl = get_unaligned_be16(&buf[2]); | 336 | bd_dl = get_unaligned_be16(&buf[2]); |
340 | 337 | ||
341 | ptr = &buf[offset]; | 338 | size = min(size - 8, bd_dl); |
342 | pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu" | 339 | |
340 | /* First UNMAP block descriptor starts at 8 byte offset */ | ||
341 | ptr = &buf[8]; | ||
342 | pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u" | ||
343 | " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr); | 343 | " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr); |
344 | 344 | ||
345 | while (size) { | 345 | while (size >= 16) { |
346 | lba = get_unaligned_be64(&ptr[0]); | 346 | lba = get_unaligned_be64(&ptr[0]); |
347 | range = get_unaligned_be32(&ptr[8]); | 347 | range = get_unaligned_be32(&ptr[8]); |
348 | pr_debug("UNMAP: Using lba: %llu and range: %u\n", | 348 | pr_debug("UNMAP: Using lba: %llu and range: %u\n", |