| From: Roland Dreier <roland@purestorage.com> |
| Date: Mon, 16 Jul 2012 15:34:24 -0700 |
| Subject: target: Fix possible integer underflow in UNMAP emulation |
| |
| From: Roland Dreier <roland@purestorage.com> |
| |
| 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 | 20 ++++++++++---------- |
| 1 file changed, 10 insertions(+), 10 deletions(-) |
| |
| --- a/drivers/target/target_core_cdb.c |
| +++ b/drivers/target/target_core_cdb.c |
| @@ -1014,9 +1014,10 @@ int target_emulate_unmap(struct se_cmd * |
| 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", |
| @@ -1025,20 +1026,19 @@ int target_emulate_unmap(struct se_cmd * |
| 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", |