add patch propagate-error-from-dquot_initialize-in-EXT4_IOC_FSSETXATTR
[ext4-patch-queue.git] / fix-EXT4_IOC_SWAP_BOOT
blobeddd4f2aec651074d25cedf51130ada73dfe7f55
1 ext4: fix EXT4_IOC_SWAP_BOOT
3 The code EXT4_IOC_SWAP_BOOT ioctl hasn't been updated in a while, and
4 it's a bit broken with respect to more modern ext4 kernels, especially
5 metadata checksums.
7 Other problems fixed with this commit:
9 * Don't allow installing a DAX, swap file, or an encrypted file as a
10   boot loader.
12 * Respect the immutable and append-only flags.
14 * Wait until any DIO operations are finished *before* calling
15   truncate_inode_pages().
17 * Don't swap inode->i_flags, since these flags have nothing to do with
18   the inode blocks --- and it will give the IMA/audit code heartburn
19   when the inode is evicted.
21 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
22 Cc: stable@kernel.org
23 Reported-by: syzbot+e81ccd4744c6c4f71354@syzkaller.appspotmail.com
24 ---
25  fs/ext4/ioctl.c | 33 +++++++++++++++++++++++++++------
26  1 file changed, 27 insertions(+), 6 deletions(-)
28 diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
29 index a7074115d6f6..d7ed7487e630 100644
30 --- a/fs/ext4/ioctl.c
31 +++ b/fs/ext4/ioctl.c
32 @@ -67,7 +67,6 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2)
33         ei1 = EXT4_I(inode1);
34         ei2 = EXT4_I(inode2);
36 -       swap(inode1->i_flags, inode2->i_flags);
37         swap(inode1->i_version, inode2->i_version);
38         swap(inode1->i_blocks, inode2->i_blocks);
39         swap(inode1->i_bytes, inode2->i_bytes);
40 @@ -85,6 +84,21 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2)
41         i_size_write(inode2, isize);
42  }
44 +static void reset_inode_seed(struct inode *inode)
46 +       struct ext4_inode_info *ei = EXT4_I(inode);
47 +       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
48 +       __le32 inum = cpu_to_le32(inode->i_ino);
49 +       __le32 gen = cpu_to_le32(inode->i_generation);
50 +       __u32 csum;
52 +       if (!ext4_has_metadata_csum(inode->i_sb))
53 +               return;
55 +       csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, sizeof(inum));
56 +       ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, sizeof(gen));
59  /**
60   * Swap the information from the given @inode and the inode
61   * EXT4_BOOT_LOADER_INO. It will basically swap i_data and all other
62 @@ -102,10 +116,13 @@ static long swap_inode_boot_loader(struct super_block *sb,
63         struct inode *inode_bl;
64         struct ext4_inode_info *ei_bl;
66 -       if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode))
67 +       if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) ||
68 +           IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) ||
69 +           ext4_has_inline_data(inode))
70                 return -EINVAL;
72 -       if (!inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN))
73 +       if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) ||
74 +           !inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN))
75                 return -EPERM;
77         inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO);
78 @@ -120,13 +137,13 @@ static long swap_inode_boot_loader(struct super_block *sb,
79          * that only 1 swap_inode_boot_loader is running. */
80         lock_two_nondirectories(inode, inode_bl);
82 -       truncate_inode_pages(&inode->i_data, 0);
83 -       truncate_inode_pages(&inode_bl->i_data, 0);
85         /* Wait for all existing dio workers */
86         inode_dio_wait(inode);
87         inode_dio_wait(inode_bl);
89 +       truncate_inode_pages(&inode->i_data, 0);
90 +       truncate_inode_pages(&inode_bl->i_data, 0);
92         handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2);
93         if (IS_ERR(handle)) {
94                 err = -EINVAL;
95 @@ -159,6 +176,8 @@ static long swap_inode_boot_loader(struct super_block *sb,
97         inode->i_generation = prandom_u32();
98         inode_bl->i_generation = prandom_u32();
99 +       reset_inode_seed(inode);
100 +       reset_inode_seed(inode_bl);
102         ext4_discard_preallocations(inode);
104 @@ -169,6 +188,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
105                         inode->i_ino, err);
106                 /* Revert all changes: */
107                 swap_inode_data(inode, inode_bl);
108 +               ext4_mark_inode_dirty(handle, inode);
109         } else {
110                 err = ext4_mark_inode_dirty(handle, inode_bl);
111                 if (err < 0) {
112 @@ -178,6 +198,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
113                         /* Revert all changes: */
114                         swap_inode_data(inode, inode_bl);
115                         ext4_mark_inode_dirty(handle, inode);
116 +                       ext4_mark_inode_dirty(handle, inode_bl);
117                 }
118         }
119         ext4_journal_stop(handle);