diff options
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); |
