| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Thu, 12 Jul 2018 19:08:05 -0400 |
| Subject: ext4: check for allocation block validity with block group locked |
| |
| commit 8d5a803c6a6ce4ec258e31f76059ea5153ba46ef upstream. |
| |
| With commit 044e6e3d74a3: "ext4: don't update checksum of new |
| initialized bitmaps" the buffer valid bit will get set without |
| actually setting up the checksum for the allocation bitmap, since the |
| checksum will get calculated once we actually allocate an inode or |
| block. |
| |
| If we are doing this, then we need to (re-)check the verified bit |
| after we take the block group lock. Otherwise, we could race with |
| another process reading and verifying the bitmap, which would then |
| complain about the checksum being invalid. |
| |
| https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1780137 |
| |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| --- a/fs/ext4/balloc.c |
| +++ b/fs/ext4/balloc.c |
| @@ -377,6 +377,8 @@ static void ext4_validate_block_bitmap(s |
| return; |
| |
| ext4_lock_group(sb, block_group); |
| + if (buffer_verified(bh)) |
| + goto verified; |
| blk = ext4_valid_block_bitmap(sb, desc, block_group, bh); |
| if (unlikely(blk != 0)) { |
| ext4_unlock_group(sb, block_group); |
| @@ -399,6 +401,7 @@ static void ext4_validate_block_bitmap(s |
| return; |
| } |
| set_buffer_verified(bh); |
| +verified: |
| ext4_unlock_group(sb, block_group); |
| } |
| |
| --- a/fs/ext4/ialloc.c |
| +++ b/fs/ext4/ialloc.c |
| @@ -166,6 +166,8 @@ ext4_read_inode_bitmap(struct super_bloc |
| |
| verify: |
| ext4_lock_group(sb, block_group); |
| + if (buffer_verified(bh)) |
| + goto verified; |
| if (!buffer_verified(bh) && |
| !ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh, |
| EXT4_INODES_PER_GROUP(sb) / 8)) { |
| @@ -183,8 +185,9 @@ verify: |
| set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state); |
| return NULL; |
| } |
| - ext4_unlock_group(sb, block_group); |
| set_buffer_verified(bh); |
| +verified: |
| + ext4_unlock_group(sb, block_group); |
| return bh; |
| } |
| |