diff options
author | Jens Axboe <jaxboe@fusionio.com> | 2010-10-29 13:31:42 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-11-10 08:40:42 -0500 |
commit | 9f864c80913467312c7b8690e41fb5ebd1b50e92 (patch) | |
tree | dea8f56529d175bc2b666b52d2f819c82abc3a19 /block/scsi_ioctl.c | |
parent | 9284bcf4e335e5f18a8bc7b26461c33ab60d0689 (diff) |
block: take care not to overflow when calculating total iov length
Reported-by: Dan Rosenberg <drosenberg@vsecurity.com>
Cc: stable@kernel.org
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'block/scsi_ioctl.c')
-rw-r--r-- | block/scsi_ioctl.c | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index a8b5a10eb5b0..4f4230b79bb6 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c | |||
@@ -321,33 +321,47 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, | |||
321 | if (hdr->iovec_count) { | 321 | if (hdr->iovec_count) { |
322 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; | 322 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; |
323 | size_t iov_data_len; | 323 | size_t iov_data_len; |
324 | struct sg_iovec *iov; | 324 | struct sg_iovec *sg_iov; |
325 | struct iovec *iov; | ||
326 | int i; | ||
325 | 327 | ||
326 | iov = kmalloc(size, GFP_KERNEL); | 328 | sg_iov = kmalloc(size, GFP_KERNEL); |
327 | if (!iov) { | 329 | if (!sg_iov) { |
328 | ret = -ENOMEM; | 330 | ret = -ENOMEM; |
329 | goto out; | 331 | goto out; |
330 | } | 332 | } |
331 | 333 | ||
332 | if (copy_from_user(iov, hdr->dxferp, size)) { | 334 | if (copy_from_user(sg_iov, hdr->dxferp, size)) { |
333 | kfree(iov); | 335 | kfree(sg_iov); |
334 | ret = -EFAULT; | 336 | ret = -EFAULT; |
335 | goto out; | 337 | goto out; |
336 | } | 338 | } |
337 | 339 | ||
340 | /* | ||
341 | * Sum up the vecs, making sure they don't overflow | ||
342 | */ | ||
343 | iov = (struct iovec *) sg_iov; | ||
344 | iov_data_len = 0; | ||
345 | for (i = 0; i < hdr->iovec_count; i++) { | ||
346 | if (iov_data_len + iov[i].iov_len < iov_data_len) { | ||
347 | kfree(sg_iov); | ||
348 | ret = -EINVAL; | ||
349 | goto out; | ||
350 | } | ||
351 | iov_data_len += iov[i].iov_len; | ||
352 | } | ||
353 | |||
338 | /* SG_IO howto says that the shorter of the two wins */ | 354 | /* SG_IO howto says that the shorter of the two wins */ |
339 | iov_data_len = iov_length((struct iovec *)iov, | ||
340 | hdr->iovec_count); | ||
341 | if (hdr->dxfer_len < iov_data_len) { | 355 | if (hdr->dxfer_len < iov_data_len) { |
342 | hdr->iovec_count = iov_shorten((struct iovec *)iov, | 356 | hdr->iovec_count = iov_shorten(iov, |
343 | hdr->iovec_count, | 357 | hdr->iovec_count, |
344 | hdr->dxfer_len); | 358 | hdr->dxfer_len); |
345 | iov_data_len = hdr->dxfer_len; | 359 | iov_data_len = hdr->dxfer_len; |
346 | } | 360 | } |
347 | 361 | ||
348 | ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count, | 362 | ret = blk_rq_map_user_iov(q, rq, NULL, sg_iov, hdr->iovec_count, |
349 | iov_data_len, GFP_KERNEL); | 363 | iov_data_len, GFP_KERNEL); |
350 | kfree(iov); | 364 | kfree(sg_iov); |
351 | } else if (hdr->dxfer_len) | 365 | } else if (hdr->dxfer_len) |
352 | ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, | 366 | ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, |
353 | GFP_KERNEL); | 367 | GFP_KERNEL); |