]> git.hungrycats.org Git - linux/commitdiff
ext4: avoid modifying checksum fields directly during checksum verification
authorDaeho Jeong <daeho.jeong@samsung.com>
Sun, 3 Jul 2016 21:51:39 +0000 (17:51 -0400)
committerJiri Slaby <jslaby@suse.cz>
Thu, 29 Sep 2016 09:14:06 +0000 (11:14 +0200)
commit b47820edd1634dc1208f9212b7ecfb4230610a23 upstream.

We temporally change checksum fields in buffers of some types of
metadata into '0' for verifying the checksum values. By doing this
without locking the buffer, some metadata's checksums, which are
being committed or written back to the storage, could be damaged.
In our test, several metadata blocks were found with damaged metadata
checksum value during recovery process. When we only verify the
checksum value, we have to avoid modifying checksum fields directly.

Signed-off-by: Daeho Jeong <daeho.jeong@samsung.com>
Signed-off-by: Youngjin Gil <youngjin.gil@samsung.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/super.c
fs/ext4/xattr.c

index 98ba65482e46c53fdda59144f8774ce16bc69c7b..aa9a1e7b02553e3df2edb5c2d92f041c66107874 100644 (file)
@@ -53,25 +53,31 @@ static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw,
                              struct ext4_inode_info *ei)
 {
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
-       __u16 csum_lo;
-       __u16 csum_hi = 0;
        __u32 csum;
+       __u16 dummy_csum = 0;
+       int offset = offsetof(struct ext4_inode, i_checksum_lo);
+       unsigned int csum_size = sizeof(dummy_csum);
 
-       csum_lo = le16_to_cpu(raw->i_checksum_lo);
-       raw->i_checksum_lo = 0;
-       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
-           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
-               csum_hi = le16_to_cpu(raw->i_checksum_hi);
-               raw->i_checksum_hi = 0;
-       }
+       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw, offset);
+       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, csum_size);
+       offset += csum_size;
+       csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
+                          EXT4_GOOD_OLD_INODE_SIZE - offset);
 
-       csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw,
-                          EXT4_INODE_SIZE(inode->i_sb));
-
-       raw->i_checksum_lo = cpu_to_le16(csum_lo);
-       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
-           EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi))
-               raw->i_checksum_hi = cpu_to_le16(csum_hi);
+       if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
+               offset = offsetof(struct ext4_inode, i_checksum_hi);
+               csum = ext4_chksum(sbi, csum, (__u8 *)raw +
+                                  EXT4_GOOD_OLD_INODE_SIZE,
+                                  offset - EXT4_GOOD_OLD_INODE_SIZE);
+               if (EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) {
+                       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum,
+                                          csum_size);
+                       offset += csum_size;
+                       csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset,
+                                          EXT4_INODE_SIZE(inode->i_sb) -
+                                          offset);
+               }
+       }
 
        return csum;
 }
index 4587a1b31c93f2a4c24e59616255a2a45f43a0c6..d2b971da417c08b90f61a7b9084c1f4c49f3ddb2 100644 (file)
@@ -414,15 +414,14 @@ static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent,
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        struct ext4_inode_info *ei = EXT4_I(inode);
        __u32 csum;
-       __le32 save_csum;
        int size;
+       __u32 dummy_csum = 0;
+       int offset = offsetof(struct dx_tail, dt_checksum);
 
        size = count_offset + (count * sizeof(struct dx_entry));
-       save_csum = t->dt_checksum;
-       t->dt_checksum = 0;
        csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size);
-       csum = ext4_chksum(sbi, csum, (__u8 *)t, sizeof(struct dx_tail));
-       t->dt_checksum = save_csum;
+       csum = ext4_chksum(sbi, csum, (__u8 *)t, offset);
+       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum));
 
        return cpu_to_le32(csum);
 }
index 2fadd3fa1accedc14b355d5fa5ad521aca0cd57b..584d22c583292b84b3a63d5cffe745728c0838f0 100644 (file)
@@ -1987,22 +1987,24 @@ failed:
 static __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
                                   struct ext4_group_desc *gdp)
 {
-       int offset;
+       int offset = offsetof(struct ext4_group_desc, bg_checksum);
        __u16 crc = 0;
        __le32 le_group = cpu_to_le32(block_group);
 
        if (ext4_has_metadata_csum(sbi->s_sb)) {
                /* Use new metadata_csum algorithm */
-               __le16 save_csum;
                __u32 csum32;
+               __u16 dummy_csum = 0;
 
-               save_csum = gdp->bg_checksum;
-               gdp->bg_checksum = 0;
                csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group,
                                     sizeof(le_group));
-               csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp,
-                                    sbi->s_desc_size);
-               gdp->bg_checksum = save_csum;
+               csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp, offset);
+               csum32 = ext4_chksum(sbi, csum32, (__u8 *)&dummy_csum,
+                                    sizeof(dummy_csum));
+               offset += sizeof(dummy_csum);
+               if (offset < sbi->s_desc_size)
+                       csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp + offset,
+                                            sbi->s_desc_size - offset);
 
                crc = csum32 & 0xFFFF;
                goto out;
@@ -2013,8 +2015,6 @@ static __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
              cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM)))
                return 0;
 
-       offset = offsetof(struct ext4_group_desc, bg_checksum);
-
        crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
        crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
        crc = crc16(crc, (__u8 *)gdp, offset);
index a5d2f1b6c5c5a5e045a71737310228bc78133c44..e5835f6e1466d9b206c514e63fb525898371d743 100644 (file)
@@ -123,17 +123,18 @@ static __le32 ext4_xattr_block_csum(struct inode *inode,
 {
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        __u32 csum;
-       __le32 save_csum;
        __le64 dsk_block_nr = cpu_to_le64(block_nr);
+       __u32 dummy_csum = 0;
+       int offset = offsetof(struct ext4_xattr_header, h_checksum);
 
-       save_csum = hdr->h_checksum;
-       hdr->h_checksum = 0;
        csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr,
                           sizeof(dsk_block_nr));
-       csum = ext4_chksum(sbi, csum, (__u8 *)hdr,
-                          EXT4_BLOCK_SIZE(inode->i_sb));
+       csum = ext4_chksum(sbi, csum, (__u8 *)hdr, offset);
+       csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum));
+       offset += sizeof(dummy_csum);
+       csum = ext4_chksum(sbi, csum, (__u8 *)hdr + offset,
+                          EXT4_BLOCK_SIZE(inode->i_sb) - offset);
 
-       hdr->h_checksum = save_csum;
        return cpu_to_le32(csum);
 }