Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Dmitry Monakhov and committed by
Jens Axboe
7035b5df 0c614e2d

+30 -16
+30 -16
drivers/block/loop.c
··· 161 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 168 /* Compute loopsize in bytes */ 169 169 size = i_size_read(file->f_mapping->host); 170 - offset = lo->lo_offset; 171 170 loopsize = size - offset; 172 - if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize) 173 - loopsize = lo->lo_sizelimit; 171 + /* offset is beyond i_size, wierd but possible */ 172 + if (loopsize < 0) 173 + return 0; 174 174 175 + if (sizelimit > 0 && sizelimit < loopsize) 176 + loopsize = sizelimit; 175 177 /* 176 178 * Unfortunately, if we want to do I/O on the device, 177 179 * the number of 512-byte sectors has to fit into a sector_t. ··· 181 179 return loopsize >> 9; 182 180 } 183 181 184 - static int 185 - figure_loop_size(struct loop_device *lo) 182 + static loff_t get_loop_size(struct loop_device *lo, struct file *file) 186 183 { 187 - loff_t size = get_loop_size(lo, lo->lo_backing_file); 184 + return get_size(lo->lo_offset, lo->lo_sizelimit, file); 185 + } 186 + 187 + static int 188 + figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit) 189 + { 190 + loff_t size = get_size(offset, sizelimit, lo->lo_backing_file); 188 191 sector_t x = (sector_t)size; 189 192 190 193 if (unlikely((loff_t)x != size)) 191 194 return -EFBIG; 192 - 195 + if (lo->lo_offset != offset) 196 + lo->lo_offset = offset; 197 + if (lo->lo_sizelimit != sizelimit) 198 + lo->lo_sizelimit = sizelimit; 193 199 set_capacity(lo->lo_disk, x); 194 - return 0; 200 + return 0; 195 201 } 196 202 197 203 static inline int ··· 1069 1059 1070 1060 if (lo->lo_offset != info->lo_offset || 1071 1061 lo->lo_sizelimit != info->lo_sizelimit) { 1072 - lo->lo_offset = info->lo_offset; 1073 - lo->lo_sizelimit = info->lo_sizelimit; 1074 - if (figure_loop_size(lo)) 1062 + if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) 1075 1063 return -EFBIG; 1076 1064 } 1077 1065 loop_config_discard(lo); ··· 1255 1247 err = -ENXIO; 1256 1248 if (unlikely(lo->lo_state != Lo_bound)) 1257 1249 goto out; 1258 - err = figure_loop_size(lo); 1250 + err = figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit); 1259 1251 if (unlikely(err)) 1260 1252 goto out; 1261 1253 sec = get_capacity(lo->lo_disk); ··· 1293 1285 goto out_unlocked; 1294 1286 break; 1295 1287 case LOOP_SET_STATUS: 1296 - err = loop_set_status_old(lo, (struct loop_info __user *) arg); 1288 + err = -EPERM; 1289 + if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) 1290 + err = loop_set_status_old(lo, 1291 + (struct loop_info __user *)arg); 1297 1292 break; 1298 1293 case LOOP_GET_STATUS: 1299 1294 err = loop_get_status_old(lo, (struct loop_info __user *) arg); 1300 1295 break; 1301 1296 case LOOP_SET_STATUS64: 1302 - err = loop_set_status64(lo, (struct loop_info64 __user *) arg); 1297 + err = -EPERM; 1298 + if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) 1299 + err = loop_set_status64(lo, 1300 + (struct loop_info64 __user *) arg); 1303 1301 break; 1304 1302 case LOOP_GET_STATUS64: 1305 1303 err = loop_get_status64(lo, (struct loop_info64 __user *) arg);