diff options
author | Dmitry Monakhov <dmonakhov@openvz.org> | 2011-11-16 03:21:49 -0500 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2011-11-16 03:21:49 -0500 |
commit | 7035b5df3c071ccaf2f1694b96bd8958b0eb37ca (patch) | |
tree | 146e916bce5b11232afac2319c012b1fccba54d0 /drivers/block/loop.c | |
parent | 0c614e2d3e6ee6ff13c6181f380787cea1d82d1d (diff) |
loop: cleanup set_status interface
1) Anyone who has read access to loopdev has permission to call set_status
and may change important parameters such as lo_offset, lo_sizelimit and
so on, which contradicts to read access pattern and definitely equals
to write access pattern.
2) Add lo_offset over i_size check to prevent blkdev_size overflow.
##Testcase_bagin
#dd if=/dev/zero of=./file bs=1k count=1
#losetup /dev/loop0 ./file
/* userspace_application */
struct loop_info64 loinf;
fd = open("/dev/loop0", O_RDONLY);
ioctl(fd, LOOP_GET_STATUS64, &loinf);
/* Set offset to any value which is bigger than i_size, and sizelimit
* to nonzero value*/
loinf.lo_offset = 4096*1024;
loinf.lo_sizelimit = 1024;
ioctl(fd, LOOP_SET_STATUS64, &loinf);
/* After this loop device will have size similar to 0x7fffffffffxxxx */
#blockdev --getsz /dev/loop0
##OUTPUT: 36028797018955968
##Testcase_end
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'drivers/block/loop.c')
-rw-r--r-- | drivers/block/loop.c | 44 |
1 files changed, 29 insertions, 15 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 0d567397c254..68b205a9338f 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -161,17 +161,19 @@ static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = { | |||
161 | &xor_funcs | 161 | &xor_funcs |
162 | }; | 162 | }; |
163 | 163 | ||
164 | static loff_t get_loop_size(struct loop_device *lo, struct file *file) | 164 | static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file) |
165 | { | 165 | { |
166 | loff_t size, offset, loopsize; | 166 | loff_t size, loopsize; |
167 | 167 | ||
168 | /* Compute loopsize in bytes */ | 168 | /* Compute loopsize in bytes */ |
169 | size = i_size_read(file->f_mapping->host); | 169 | size = i_size_read(file->f_mapping->host); |
170 | offset = lo->lo_offset; | ||
171 | loopsize = size - offset; | 170 | loopsize = size - offset; |
172 | if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize) | 171 | /* offset is beyond i_size, wierd but possible */ |
173 | loopsize = lo->lo_sizelimit; | 172 | if (loopsize < 0) |
173 | return 0; | ||
174 | 174 | ||
175 | if (sizelimit > 0 && sizelimit < loopsize) | ||
176 | loopsize = sizelimit; | ||
175 | /* | 177 | /* |
176 | * Unfortunately, if we want to do I/O on the device, | 178 | * Unfortunately, if we want to do I/O on the device, |
177 | * the number of 512-byte sectors has to fit into a sector_t. | 179 | * the number of 512-byte sectors has to fit into a sector_t. |
@@ -179,17 +181,25 @@ static loff_t get_loop_size(struct loop_device *lo, struct file *file) | |||
179 | return loopsize >> 9; | 181 | return loopsize >> 9; |
180 | } | 182 | } |
181 | 183 | ||
184 | static loff_t get_loop_size(struct loop_device *lo, struct file *file) | ||
185 | { | ||
186 | return get_size(lo->lo_offset, lo->lo_sizelimit, file); | ||
187 | } | ||
188 | |||
182 | static int | 189 | static int |
183 | figure_loop_size(struct loop_device *lo) | 190 | figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit) |
184 | { | 191 | { |
185 | loff_t size = get_loop_size(lo, lo->lo_backing_file); | 192 | loff_t size = get_size(offset, sizelimit, lo->lo_backing_file); |
186 | sector_t x = (sector_t)size; | 193 | sector_t x = (sector_t)size; |
187 | 194 | ||
188 | if (unlikely((loff_t)x != size)) | 195 | if (unlikely((loff_t)x != size)) |
189 | return -EFBIG; | 196 | return -EFBIG; |
190 | 197 | if (lo->lo_offset != offset) | |
198 | lo->lo_offset = offset; | ||
199 | if (lo->lo_sizelimit != sizelimit) | ||
200 | lo->lo_sizelimit = sizelimit; | ||
191 | set_capacity(lo->lo_disk, x); | 201 | set_capacity(lo->lo_disk, x); |
192 | return 0; | 202 | return 0; |
193 | } | 203 | } |
194 | 204 | ||
195 | static inline int | 205 | static inline int |
@@ -1059,9 +1069,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) | |||
1059 | 1069 | ||
1060 | if (lo->lo_offset != info->lo_offset || | 1070 | if (lo->lo_offset != info->lo_offset || |
1061 | lo->lo_sizelimit != info->lo_sizelimit) { | 1071 | lo->lo_sizelimit != info->lo_sizelimit) { |
1062 | lo->lo_offset = info->lo_offset; | 1072 | if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) |
1063 | lo->lo_sizelimit = info->lo_sizelimit; | ||
1064 | if (figure_loop_size(lo)) | ||
1065 | return -EFBIG; | 1073 | return -EFBIG; |
1066 | } | 1074 | } |
1067 | loop_config_discard(lo); | 1075 | loop_config_discard(lo); |
@@ -1247,7 +1255,7 @@ static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev) | |||
1247 | err = -ENXIO; | 1255 | err = -ENXIO; |
1248 | if (unlikely(lo->lo_state != Lo_bound)) | 1256 | if (unlikely(lo->lo_state != Lo_bound)) |
1249 | goto out; | 1257 | goto out; |
1250 | err = figure_loop_size(lo); | 1258 | err = figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit); |
1251 | if (unlikely(err)) | 1259 | if (unlikely(err)) |
1252 | goto out; | 1260 | goto out; |
1253 | sec = get_capacity(lo->lo_disk); | 1261 | sec = get_capacity(lo->lo_disk); |
@@ -1285,13 +1293,19 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, | |||
1285 | goto out_unlocked; | 1293 | goto out_unlocked; |
1286 | break; | 1294 | break; |
1287 | case LOOP_SET_STATUS: | 1295 | case LOOP_SET_STATUS: |
1288 | err = loop_set_status_old(lo, (struct loop_info __user *) arg); | 1296 | err = -EPERM; |
1297 | if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) | ||
1298 | err = loop_set_status_old(lo, | ||
1299 | (struct loop_info __user *)arg); | ||
1289 | break; | 1300 | break; |
1290 | case LOOP_GET_STATUS: | 1301 | case LOOP_GET_STATUS: |
1291 | err = loop_get_status_old(lo, (struct loop_info __user *) arg); | 1302 | err = loop_get_status_old(lo, (struct loop_info __user *) arg); |
1292 | break; | 1303 | break; |
1293 | case LOOP_SET_STATUS64: | 1304 | case LOOP_SET_STATUS64: |
1294 | err = loop_set_status64(lo, (struct loop_info64 __user *) arg); | 1305 | err = -EPERM; |
1306 | if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) | ||
1307 | err = loop_set_status64(lo, | ||
1308 | (struct loop_info64 __user *) arg); | ||
1295 | break; | 1309 | break; |
1296 | case LOOP_GET_STATUS64: | 1310 | case LOOP_GET_STATUS64: |
1297 | err = loop_get_status64(lo, (struct loop_info64 __user *) arg); | 1311 | err = loop_get_status64(lo, (struct loop_info64 __user *) arg); |