add patch ext2-file-fs-deadlock-while-reading-corrupted-xattr-block
[ext4-patch-queue.git] / avoid-modifying-checksum-fields-directly-during-checksum-verification
blob22d976cd7cd6db4a9e06d550dfb619c1a4fe308a
1 ext4: avoid modifying checksum fields directly during checksum verification
3 From: Daeho Jeong <daeho.jeong@samsung.com>
5 We temporally change checksum fields in buffers of some types of
6 metadata into '0' for verifying the checksum values. By doing this
7 without locking the buffer, some metadata's checksums, which are
8 being committed or written back to the storage, could be damaged.
9 In our test, several metadata blocks were found with damaged metadata
10 checksum value during recovery process. When we only verify the
11 checksum value, we have to avoid modifying checksum fields directly.
13 Signed-off-by: Daeho Jeong <daeho.jeong@samsung.com>
14 Signed-off-by: Youngjin Gil <youngjin.gil@samsung.com>
15 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
16 Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
17 ---
18  fs/ext4/inode.c |   38 ++++++++++++++++++++++----------------
19  fs/ext4/namei.c |    9 ++++-----
20  fs/ext4/super.c |   18 +++++++++---------
21  fs/ext4/xattr.c |   13 +++++++------
22  4 files changed, 42 insertions(+), 36 deletions(-)
24 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
25 index 971892d..5ca71aa 100644
26 --- a/fs/ext4/inode.c
27 +++ b/fs/ext4/inode.c
28 @@ -51,25 +51,31 @@ static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw,
29                               struct ext4_inode_info *ei)
30  {
31         struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
32 -       __u16 csum_lo;
33 -       __u16 csum_hi = 0;
34         __u32 csum;
35 +       __u16 dummy_csum = 0;
36 +       int offset = offsetof(struct ext4_inode, i_checksum_lo);
37 +       unsigned int csum_size = sizeof(dummy_csum);
39 -       csum_lo = le16_to_cpu(raw->i_checksum_lo);
40 -       raw->i_checksum_lo = 0;
41 -       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
42 -           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
43 -               csum_hi = le16_to_cpu(raw->i_checksum_hi);
44 -               raw->i_checksum_hi = 0;
45 -       }
46 +       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw, offset);
47 +       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, csum_size);
48 +       offset += csum_size;
49 +       csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
50 +                          EXT4_GOOD_OLD_INODE_SIZE - offset);
52 -       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw,
53 -                          EXT4_INODE_SIZE(inode->i_sb));
55 -       raw->i_checksum_lo = cpu_to_le16(csum_lo);
56 -       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
57 -           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
58 -               raw->i_checksum_hi = cpu_to_le16(csum_hi);
59 +       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
60 +               offset = offsetof(struct ext4_inode, i_checksum_hi);
61 +               csum = ext4_chksum(sbi, csum, (__u8 *)raw +
62 +                                  EXT4_GOOD_OLD_INODE_SIZE,
63 +                                  offset - EXT4_GOOD_OLD_INODE_SIZE);
64 +               if (EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
65 +                       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum,
66 +                                          csum_size);
67 +                       offset += csum_size;
68 +                       csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
69 +                                          EXT4_INODE_SIZE(inode->i_sb) -
70 +                                          offset);
71 +               }
72 +       }
74         return csum;
75  }
76 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
77 index ec811bb..4a918f8 100644
78 --- a/fs/ext4/namei.c
79 +++ b/fs/ext4/namei.c
80 @@ -420,15 +420,14 @@ static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent,
81         struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
82         struct ext4_inode_info *ei = EXT4_I(inode);
83         __u32 csum;
84 -       __le32 save_csum;
85         int size;
86 +       __u32 dummy_csum = 0;
87 +       int offset = offsetof(struct dx_tail, dt_checksum);
89         size = count_offset + (count * sizeof(struct dx_entry));
90 -       save_csum = t->dt_checksum;
91 -       t->dt_checksum = 0;
92         csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size);
93 -       csum = ext4_chksum(sbi, csum, (__u8 *)t, sizeof(struct dx_tail));
94 -       t->dt_checksum = save_csum;
95 +       csum = ext4_chksum(sbi, csum, (__u8 *)t, offset);
96 +       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum));
98         return cpu_to_le32(csum);
99  }
100 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
101 index de02a9e..b6cb89a 100644
102 --- a/fs/ext4/super.c
103 +++ b/fs/ext4/super.c
104 @@ -2058,23 +2058,25 @@ failed:
105  static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group,
106                                    struct ext4_group_desc *gdp)
108 -       int offset;
109 +       int offset = offsetof(struct ext4_group_desc, bg_checksum);
110         __u16 crc = 0;
111         __le32 le_group = cpu_to_le32(block_group);
112         struct ext4_sb_info *sbi = EXT4_SB(sb);
114         if (ext4_has_metadata_csum(sbi->s_sb)) {
115                 /* Use new metadata_csum algorithm */
116 -               __le16 save_csum;
117                 __u32 csum32;
118 +               __u16 dummy_csum = 0;
120 -               save_csum = gdp->bg_checksum;
121 -               gdp->bg_checksum = 0;
122                 csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group,
123                                      sizeof(le_group));
124 -               csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp,
125 -                                    sbi->s_desc_size);
126 -               gdp->bg_checksum = save_csum;
127 +               csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp, offset);
128 +               csum32 = ext4_chksum(sbi, csum32, (__u8 *)&dummy_csum,
129 +                                    sizeof(dummy_csum));
130 +               offset += sizeof(dummy_csum);
131 +               if (offset < sbi->s_desc_size)
132 +                       csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp + offset,
133 +                                            sbi->s_desc_size - offset);
135                 crc = csum32 & 0xFFFF;
136                 goto out;
137 @@ -2084,8 +2086,6 @@ static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group,
138         if (!ext4_has_feature_gdt_csum(sb))
139                 return 0;
141 -       offset = offsetof(struct ext4_group_desc, bg_checksum);
143         crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
144         crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
145         crc = crc16(crc, (__u8 *)gdp, offset);
146 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
147 index 0441e05..988b379 100644
148 --- a/fs/ext4/xattr.c
149 +++ b/fs/ext4/xattr.c
150 @@ -121,17 +121,18 @@ static __le32 ext4_xattr_block_csum(struct inode *inode,
152         struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
153         __u32 csum;
154 -       __le32 save_csum;
155         __le64 dsk_block_nr = cpu_to_le64(block_nr);
156 +       __u32 dummy_csum = 0;
157 +       int offset = offsetof(struct ext4_xattr_header, h_checksum);
159 -       save_csum = hdr->h_checksum;
160 -       hdr->h_checksum = 0;
161         csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr,
162                            sizeof(dsk_block_nr));
163 -       csum = ext4_chksum(sbi, csum, (__u8 *)hdr,
164 -                          EXT4_BLOCK_SIZE(inode->i_sb));
165 +       csum = ext4_chksum(sbi, csum, (__u8 *)hdr, offset);
166 +       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum));
167 +       offset += sizeof(dummy_csum);
168 +       csum = ext4_chksum(sbi, csum, (__u8 *)hdr + offset,
169 +                          EXT4_BLOCK_SIZE(inode->i_sb) - offset);
171 -       hdr->h_checksum = save_csum;
172         return cpu_to_le32(csum);
175 -- 
176 1.7.9.5