4.14-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jaegeuk Kim jaegeuk@kernel.org
commit 5db470e229e22b7eda6e23b5566e532c96fb5bc3 upstream.
If we don't drop caches used in old offset or block_size, we can get old data from new offset/block_size, which gives unexpected data to user.
For example, Martijn found a loopback bug in the below scenario. 1) LOOP_SET_FD loads first two pages on loop file 2) LOOP_SET_STATUS64 changes the offset on the loop file 3) mount is failed due to the cached pages having wrong superblock
Cc: Jens Axboe axboe@kernel.dk Cc: linux-block@vger.kernel.org Reported-by: Martijn Coenen maco@google.com Reviewed-by: Bart Van Assche bvanassche@acm.org Signed-off-by: Jaegeuk Kim jaegeuk@kernel.org Signed-off-by: Jens Axboe axboe@kernel.dk Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org
--- drivers/block/loop.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-)
--- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1097,6 +1097,12 @@ loop_set_status(struct loop_device *lo, if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE) return -EINVAL;
+ if (lo->lo_offset != info->lo_offset || + lo->lo_sizelimit != info->lo_sizelimit) { + sync_blockdev(lo->lo_device); + kill_bdev(lo->lo_device); + } + /* I/O need to be drained during transfer transition */ blk_mq_freeze_queue(lo->lo_queue);
@@ -1125,6 +1131,14 @@ loop_set_status(struct loop_device *lo,
if (lo->lo_offset != info->lo_offset || lo->lo_sizelimit != info->lo_sizelimit) { + /* kill_bdev should have truncated all the pages */ + if (lo->lo_device->bd_inode->i_mapping->nrpages) { + err = -EAGAIN; + pr_warn("%s: loop%d (%s) has still dirty pages (nrpages=%lu)\n", + __func__, lo->lo_number, lo->lo_file_name, + lo->lo_device->bd_inode->i_mapping->nrpages); + goto exit; + } if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { err = -EFBIG; goto exit; @@ -1346,22 +1360,39 @@ static int loop_set_dio(struct loop_devi
static int loop_set_block_size(struct loop_device *lo, unsigned long arg) { + int err = 0; + if (lo->lo_state != Lo_bound) return -ENXIO;
if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg)) return -EINVAL;
+ if (lo->lo_queue->limits.logical_block_size != arg) { + sync_blockdev(lo->lo_device); + kill_bdev(lo->lo_device); + } + blk_mq_freeze_queue(lo->lo_queue);
+ /* kill_bdev should have truncated all the pages */ + if (lo->lo_queue->limits.logical_block_size != arg && + lo->lo_device->bd_inode->i_mapping->nrpages) { + err = -EAGAIN; + pr_warn("%s: loop%d (%s) has still dirty pages (nrpages=%lu)\n", + __func__, lo->lo_number, lo->lo_file_name, + lo->lo_device->bd_inode->i_mapping->nrpages); + goto out_unfreeze; + } + blk_queue_logical_block_size(lo->lo_queue, arg); blk_queue_physical_block_size(lo->lo_queue, arg); blk_queue_io_min(lo->lo_queue, arg); loop_update_dio(lo); - +out_unfreeze: blk_mq_unfreeze_queue(lo->lo_queue);
- return 0; + return err; }
static int lo_ioctl(struct block_device *bdev, fmode_t mode,