]>
Commit | Line | Data |
---|---|---|
d476d390 GKH |
1 | From 9f864c80913467312c7b8690e41fb5ebd1b50e92 Mon Sep 17 00:00:00 2001 |
2 | From: Jens Axboe <jaxboe@fusionio.com> | |
3 | Date: Fri, 29 Oct 2010 11:31:42 -0600 | |
4 | Subject: block: take care not to overflow when calculating total iov length | |
5 | ||
6 | From: Jens Axboe <jaxboe@fusionio.com> | |
7 | ||
8 | commit 9f864c80913467312c7b8690e41fb5ebd1b50e92 upstream. | |
9 | ||
10 | Reported-by: Dan Rosenberg <drosenberg@vsecurity.com> | |
11 | Signed-off-by: Jens Axboe <jaxboe@fusionio.com> | |
12 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
13 | ||
14 | --- | |
15 | block/scsi_ioctl.c | 34 ++++++++++++++++++++++++---------- | |
16 | 1 file changed, 24 insertions(+), 10 deletions(-) | |
17 | ||
18 | --- a/block/scsi_ioctl.c | |
19 | +++ b/block/scsi_ioctl.c | |
20 | @@ -321,33 +321,47 @@ static int sg_io(struct request_queue *q | |
21 | if (hdr->iovec_count) { | |
22 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; | |
23 | size_t iov_data_len; | |
24 | - struct sg_iovec *iov; | |
25 | + struct sg_iovec *sg_iov; | |
26 | + struct iovec *iov; | |
27 | + int i; | |
28 | ||
29 | - iov = kmalloc(size, GFP_KERNEL); | |
30 | - if (!iov) { | |
31 | + sg_iov = kmalloc(size, GFP_KERNEL); | |
32 | + if (!sg_iov) { | |
33 | ret = -ENOMEM; | |
34 | goto out; | |
35 | } | |
36 | ||
37 | - if (copy_from_user(iov, hdr->dxferp, size)) { | |
38 | - kfree(iov); | |
39 | + if (copy_from_user(sg_iov, hdr->dxferp, size)) { | |
40 | + kfree(sg_iov); | |
41 | ret = -EFAULT; | |
42 | goto out; | |
43 | } | |
44 | ||
45 | + /* | |
46 | + * Sum up the vecs, making sure they don't overflow | |
47 | + */ | |
48 | + iov = (struct iovec *) sg_iov; | |
49 | + iov_data_len = 0; | |
50 | + for (i = 0; i < hdr->iovec_count; i++) { | |
51 | + if (iov_data_len + iov[i].iov_len < iov_data_len) { | |
52 | + kfree(sg_iov); | |
53 | + ret = -EINVAL; | |
54 | + goto out; | |
55 | + } | |
56 | + iov_data_len += iov[i].iov_len; | |
57 | + } | |
58 | + | |
59 | /* SG_IO howto says that the shorter of the two wins */ | |
60 | - iov_data_len = iov_length((struct iovec *)iov, | |
61 | - hdr->iovec_count); | |
62 | if (hdr->dxfer_len < iov_data_len) { | |
63 | - hdr->iovec_count = iov_shorten((struct iovec *)iov, | |
64 | + hdr->iovec_count = iov_shorten(iov, | |
65 | hdr->iovec_count, | |
66 | hdr->dxfer_len); | |
67 | iov_data_len = hdr->dxfer_len; | |
68 | } | |
69 | ||
70 | - ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count, | |
71 | + ret = blk_rq_map_user_iov(q, rq, NULL, sg_iov, hdr->iovec_count, | |
72 | iov_data_len, GFP_KERNEL); | |
73 | - kfree(iov); | |
74 | + kfree(sg_iov); | |
75 | } else if (hdr->dxfer_len) | |
76 | ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, | |
77 | GFP_KERNEL); |