diff options
author | Theodore Ts'o <tytso@mit.edu> | 2018-05-07 11:37:58 -0400 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2018-06-05 11:08:21 -0400 |
commit | d2ac838e4cd7e5e9891ecc094d626734b0245c99 (patch) | |
tree | b93df5a59844ad585b25dad0ee85890a2cc0fbae | |
parent | d377535405686f735b90a8ad4ba269484cd7c96e (diff) |
loop: add recursion validation to LOOP_CHANGE_FD
Refactor the validation code used in LOOP_SET_FD so it is also used in
LOOP_CHANGE_FD. Otherwise it is possible to construct a set of loop
devices that all refer to each other. This can lead to a infinite
loop in starting with "while (is_loop_device(f)) .." in loop_set_fd().
Fix this by refactoring out the validation code and using it for
LOOP_CHANGE_FD as well as LOOP_SET_FD.
Reported-by: syzbot+4349872271ece473a7c91190b68b4bac7c5dbc87@syzkaller.appspotmail.com
Reported-by: syzbot+40bd32c4d9a3cc12a339@syzkaller.appspotmail.com
Reported-by: syzbot+769c54e66f994b041be7@syzkaller.appspotmail.com
Reported-by: syzbot+0a89a9ce473936c57065@syzkaller.appspotmail.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r-- | drivers/block/loop.c | 68 |
1 files changed, 38 insertions, 30 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 4838b0dbaad3..f8f3ca6e77fd 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -644,6 +644,36 @@ static void loop_reread_partitions(struct loop_device *lo, | |||
644 | __func__, lo->lo_number, lo->lo_file_name, rc); | 644 | __func__, lo->lo_number, lo->lo_file_name, rc); |
645 | } | 645 | } |
646 | 646 | ||
647 | static inline int is_loop_device(struct file *file) | ||
648 | { | ||
649 | struct inode *i = file->f_mapping->host; | ||
650 | |||
651 | return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; | ||
652 | } | ||
653 | |||
654 | static int loop_validate_file(struct file *file, struct block_device *bdev) | ||
655 | { | ||
656 | struct inode *inode = file->f_mapping->host; | ||
657 | struct file *f = file; | ||
658 | |||
659 | /* Avoid recursion */ | ||
660 | while (is_loop_device(f)) { | ||
661 | struct loop_device *l; | ||
662 | |||
663 | if (f->f_mapping->host->i_bdev == bdev) | ||
664 | return -EBADF; | ||
665 | |||
666 | l = f->f_mapping->host->i_bdev->bd_disk->private_data; | ||
667 | if (l->lo_state == Lo_unbound) { | ||
668 | return -EINVAL; | ||
669 | } | ||
670 | f = l->lo_backing_file; | ||
671 | } | ||
672 | if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) | ||
673 | return -EINVAL; | ||
674 | return 0; | ||
675 | } | ||
676 | |||
647 | /* | 677 | /* |
648 | * loop_change_fd switched the backing store of a loopback device to | 678 | * loop_change_fd switched the backing store of a loopback device to |
649 | * a new file. This is useful for operating system installers to free up | 679 | * a new file. This is useful for operating system installers to free up |
@@ -673,14 +703,15 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, | |||
673 | if (!file) | 703 | if (!file) |
674 | goto out; | 704 | goto out; |
675 | 705 | ||
706 | error = loop_validate_file(file, bdev); | ||
707 | if (error) | ||
708 | goto out_putf; | ||
709 | |||
676 | inode = file->f_mapping->host; | 710 | inode = file->f_mapping->host; |
677 | old_file = lo->lo_backing_file; | 711 | old_file = lo->lo_backing_file; |
678 | 712 | ||
679 | error = -EINVAL; | 713 | error = -EINVAL; |
680 | 714 | ||
681 | if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) | ||
682 | goto out_putf; | ||
683 | |||
684 | /* size of the new backing store needs to be the same */ | 715 | /* size of the new backing store needs to be the same */ |
685 | if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) | 716 | if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) |
686 | goto out_putf; | 717 | goto out_putf; |
@@ -706,13 +737,6 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, | |||
706 | return error; | 737 | return error; |
707 | } | 738 | } |
708 | 739 | ||
709 | static inline int is_loop_device(struct file *file) | ||
710 | { | ||
711 | struct inode *i = file->f_mapping->host; | ||
712 | |||
713 | return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; | ||
714 | } | ||
715 | |||
716 | /* loop sysfs attributes */ | 740 | /* loop sysfs attributes */ |
717 | 741 | ||
718 | static ssize_t loop_attr_show(struct device *dev, char *page, | 742 | static ssize_t loop_attr_show(struct device *dev, char *page, |
@@ -878,7 +902,7 @@ static int loop_prepare_queue(struct loop_device *lo) | |||
878 | static int loop_set_fd(struct loop_device *lo, fmode_t mode, | 902 | static int loop_set_fd(struct loop_device *lo, fmode_t mode, |
879 | struct block_device *bdev, unsigned int arg) | 903 | struct block_device *bdev, unsigned int arg) |
880 | { | 904 | { |
881 | struct file *file, *f; | 905 | struct file *file; |
882 | struct inode *inode; | 906 | struct inode *inode; |
883 | struct address_space *mapping; | 907 | struct address_space *mapping; |
884 | int lo_flags = 0; | 908 | int lo_flags = 0; |
@@ -897,29 +921,13 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, | |||
897 | if (lo->lo_state != Lo_unbound) | 921 | if (lo->lo_state != Lo_unbound) |
898 | goto out_putf; | 922 | goto out_putf; |
899 | 923 | ||
900 | /* Avoid recursion */ | 924 | error = loop_validate_file(file, bdev); |
901 | f = file; | 925 | if (error) |
902 | while (is_loop_device(f)) { | 926 | goto out_putf; |
903 | struct loop_device *l; | ||
904 | |||
905 | if (f->f_mapping->host->i_bdev == bdev) | ||
906 | goto out_putf; | ||
907 | |||
908 | l = f->f_mapping->host->i_bdev->bd_disk->private_data; | ||
909 | if (l->lo_state == Lo_unbound) { | ||
910 | error = -EINVAL; | ||
911 | goto out_putf; | ||
912 | } | ||
913 | f = l->lo_backing_file; | ||
914 | } | ||
915 | 927 | ||
916 | mapping = file->f_mapping; | 928 | mapping = file->f_mapping; |
917 | inode = mapping->host; | 929 | inode = mapping->host; |
918 | 930 | ||
919 | error = -EINVAL; | ||
920 | if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) | ||
921 | goto out_putf; | ||
922 | |||
923 | if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) || | 931 | if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) || |
924 | !file->f_op->write_iter) | 932 | !file->f_op->write_iter) |
925 | lo_flags |= LO_FLAGS_READ_ONLY; | 933 | lo_flags |= LO_FLAGS_READ_ONLY; |