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>
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
28 @@ -51,25 +51,31 @@ static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw,
29 struct ext4_inode_info *ei)
31 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
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;
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,
67 + offset += csum_size;
68 + csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
69 + EXT4_INODE_SIZE(inode->i_sb) -
76 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
77 index ec811bb..4a918f8 100644
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);
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;
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);
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)
109 + int offset = offsetof(struct ext4_group_desc, bg_checksum);
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 */
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,
124 - csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp,
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;
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))
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);
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);