summaryrefslogtreecommitdiff |
diff options
author | Michal Marek <mmarek@suse.com> | 2016-08-08 16:24:26 +0200 |
---|---|---|
committer | Michal Marek <mmarek@suse.com> | 2016-08-08 16:24:26 +0200 |
commit | 5b281a8ed597129e66345f69780d4efcb9e62f0b (patch) | |
tree | 972c1ee1c99ab55b52b60e7026f91fc9e5e925eb | |
parent | a3843a967c6f0e538157a88d66ea906e1bb136df (diff) | |
parent | bfd4ccc2339dac2c14208c52c6f89b383ed920b6 (diff) |
Merge branch 'users/jack/SLE12-SP2/for-next' into SLE12-SP2rpm-4.4.16-56
Pull ext4 fix from Jan Kára (bsc#991671).
5 files changed, 275 insertions, 0 deletions
diff --git a/patches.fixes/ext4-Avoid-deadlock-when-expanding-inode-size.patch b/patches.fixes/ext4-Avoid-deadlock-when-expanding-inode-size.patch new file mode 100644 index 0000000000..10354a4d85 --- /dev/null +++ b/patches.fixes/ext4-Avoid-deadlock-when-expanding-inode-size.patch @@ -0,0 +1,86 @@ +From 88e430cbb2862641df2b557d51bcd142be423a57 Mon Sep 17 00:00:00 2001 +From: Jan Kara <jack@suse.cz> +Date: Fri, 29 Jul 2016 11:27:32 +0200 +Subject: [PATCH 04/11] ext4: Avoid deadlock when expanding inode size +Patch-mainline: Submitted, Aug 3 2016 +References: bsc#991671 + +When we need to move xattrs into external xattr block, we call +ext4_xattr_block_set() from ext4_expand_extra_isize_ea(). That may end +up calling ext4_mark_inode_dirty() again which will recurse back into +the inode expansion code leading to deadlocks. + +Protect from recursion using EXT4_STATE_NO_EXPAND inode flag and move +its management into ext4_expand_extra_isize_ea() since its manipulation +is safe there (due to xattr_sem) from possible races with +ext4_xattr_set_handle() which plays with it as well. + +CC: <stable@vger.kernel.org> # 4.4.x- +Signed-off-by: Jan Kara <jack@suse.cz> +--- + fs/ext4/inode.c | 2 -- + fs/ext4/xattr.c | 19 +++++++++++++------ + 2 files changed, 13 insertions(+), 8 deletions(-) + +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -5301,8 +5301,6 @@ int ext4_mark_inode_dirty(handle_t *hand + sbi->s_want_extra_isize, + iloc, handle); + if (ret) { +- ext4_set_inode_state(inode, +- EXT4_STATE_NO_EXPAND); + if (mnt_count != + le16_to_cpu(sbi->s_es->s_mnt_count)) { + ext4_warning(inode->i_sb, +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -1334,12 +1334,14 @@ int ext4_expand_extra_isize_ea(struct in + int isize_diff; /* How much do we need to grow i_extra_isize */ + + down_write(&EXT4_I(inode)->xattr_sem); ++ /* ++ * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty ++ */ ++ ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); + retry: + isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize; +- if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) { +- up_write(&EXT4_I(inode)->xattr_sem); +- return 0; +- } ++ if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) ++ goto out; + + header = IHDR(inode, raw_inode); + entry = IFIRST(header); +@@ -1364,8 +1366,7 @@ retry: + (void *)header, total_ino, + inode->i_sb->s_blocksize); + EXT4_I(inode)->i_extra_isize = new_extra_isize; +- error = 0; +- goto cleanup; ++ goto out; + } + + /* +@@ -1525,6 +1526,8 @@ retry: + kfree(bs); + } + brelse(bh); ++out: ++ ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND); + up_write(&EXT4_I(inode)->xattr_sem); + return 0; + +@@ -1536,6 +1539,10 @@ cleanup: + kfree(is); + kfree(bs); + brelse(bh); ++ /* ++ * We deliberately leave EXT4_STATE_NO_EXPAND set here since inode ++ * size expansion failed. ++ */ + up_write(&EXT4_I(inode)->xattr_sem); + return error; + } diff --git a/patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes-2.patch b/patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes-2.patch new file mode 100644 index 0000000000..cdcf9bd1d5 --- /dev/null +++ b/patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes-2.patch @@ -0,0 +1,44 @@ +From 67619f59b7438c71e251a37081b5d93c196721cf Mon Sep 17 00:00:00 2001 +From: Jan Kara <jack@suse.cz> +Date: Thu, 28 Jul 2016 16:56:08 +0200 +Subject: [PATCH 02/11] ext4: Fix xattr shifting when expanding inodes (2) +Patch-mainline: Submitted, Aug 3 2016 +References: bsc#991671 + +When multiple xattrs need to be moved out of inode, we did not properly +recompute total size of xattr headers in the inode and the new header +position. Thus when moving the second and further xattr we asked +ext4_xattr_shift_entries() to move too much and from the wrong place, +resulting in possible xattr value corruption or general memory +corruption. + +CC: <stable@vger.kernel.org> # 4.4.x- +Signed-off-by: Jan Kara <jack@suse.cz> +--- + fs/ext4/xattr.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -1488,6 +1488,7 @@ retry: + error = ext4_xattr_ibody_set(handle, inode, &i, is); + if (error) + goto cleanup; ++ total_ino -= entry_size; + + entry = IFIRST(header); + if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff) +@@ -1498,11 +1499,11 @@ retry: + ext4_xattr_shift_entries(entry, -shift_bytes, + (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE + + EXT4_I(inode)->i_extra_isize + shift_bytes, +- (void *)header, total_ino - entry_size, +- inode->i_sb->s_blocksize); ++ (void *)header, total_ino, inode->i_sb->s_blocksize); + + isize_diff -= shift_bytes; + EXT4_I(inode)->i_extra_isize += shift_bytes; ++ header = IHDR(inode, raw_inode); + + i.name = b_entry_name; + i.value = buffer; diff --git a/patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes.patch b/patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes.patch new file mode 100644 index 0000000000..d00cac3233 --- /dev/null +++ b/patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes.patch @@ -0,0 +1,111 @@ +From bc51439e119f84184bc52ab72458146357673e56 Mon Sep 17 00:00:00 2001 +From: Jan Kara <jack@suse.cz> +Date: Thu, 28 Jul 2016 16:16:02 +0200 +Subject: [PATCH 01/11] ext4: Fix xattr shifting when expanding inodes +Patch-mainline: Submitted, Aug 3 2016 +References: bsc#991671 + +The code in ext4_expand_extra_isize_ea() treated new_extra_isize +argument sometimes as the desired target i_extra_isize and sometimes as +the amount by which we need to grow current i_extra_isize. These happen +to coincide when i_extra_isize is 0 which used to be the common case and +so nobody noticed this until recently when we added i_projid to the +inode and so i_extra_isize now needs to grow from 28 to 32 bytes. + +The result of these bugs was that we sometimes unnecessarily decided to +move xattrs out of inode even if there was enough space and we often +ended up corrupting in-inode xattrs because arguments to +ext4_xattr_shift_entries() were just wrong. This could demonstrate +itself as BUG_ON in ext4_xattr_shift_entries() triggering. + +Fix the problem by introducing new isize_diff variable and use it where +appropriate. + +CC: <stable@vger.kernel.org> # 4.4.x- +Reported-by: Dave Chinner <david@fromorbit.com> +Signed-off-by: Jan Kara <jack@suse.cz> +--- + fs/ext4/xattr.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -1329,11 +1329,13 @@ int ext4_expand_extra_isize_ea(struct in + size_t min_offs, free; + int total_ino; + void *base, *start, *end; +- int extra_isize = 0, error = 0, tried_min_extra_isize = 0; ++ int error = 0, tried_min_extra_isize = 0; + int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize); ++ int isize_diff; /* How much do we need to grow i_extra_isize */ + + down_write(&EXT4_I(inode)->xattr_sem); + retry: ++ isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize; + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) { + up_write(&EXT4_I(inode)->xattr_sem); + return 0; +@@ -1354,7 +1356,7 @@ retry: + total_ino = sizeof(struct ext4_xattr_ibody_header); + + free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); +- if (free >= new_extra_isize) { ++ if (free >= isize_diff) { + entry = IFIRST(header); + ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize + - new_extra_isize, (void *)raw_inode + +@@ -1386,7 +1388,7 @@ retry: + end = bh->b_data + bh->b_size; + min_offs = end - base; + free = ext4_xattr_free_space(first, &min_offs, base, NULL); +- if (free < new_extra_isize) { ++ if (free < isize_diff) { + if (!tried_min_extra_isize && s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; +@@ -1400,7 +1402,7 @@ retry: + free = inode->i_sb->s_blocksize; + } + +- while (new_extra_isize > 0) { ++ while (isize_diff > 0) { + size_t offs, size, entry_size; + struct ext4_xattr_entry *small_entry = NULL; + struct ext4_xattr_info i = { +@@ -1431,7 +1433,7 @@ retry: + EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) + + EXT4_XATTR_LEN(last->e_name_len); + if (total_size <= free && total_size < min_total_size) { +- if (total_size < new_extra_isize) { ++ if (total_size < isize_diff) { + small_entry = last; + } else { + entry = last; +@@ -1488,20 +1490,19 @@ retry: + goto cleanup; + + entry = IFIRST(header); +- if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize) +- shift_bytes = new_extra_isize; ++ if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff) ++ shift_bytes = isize_diff; + else + shift_bytes = entry_size + size; + /* Adjust the offsets and shift the remaining entries ahead */ +- ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize - +- shift_bytes, (void *)raw_inode + +- EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes, ++ ext4_xattr_shift_entries(entry, -shift_bytes, ++ (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE + ++ EXT4_I(inode)->i_extra_isize + shift_bytes, + (void *)header, total_ino - entry_size, + inode->i_sb->s_blocksize); + +- extra_isize += shift_bytes; +- new_extra_isize -= shift_bytes; +- EXT4_I(inode)->i_extra_isize = extra_isize; ++ isize_diff -= shift_bytes; ++ EXT4_I(inode)->i_extra_isize += shift_bytes; + + i.name = b_entry_name; + i.value = buffer; diff --git a/patches.fixes/ext4-Properly-align-shifted-xattrs-when-expanding-in.patch b/patches.fixes/ext4-Properly-align-shifted-xattrs-when-expanding-in.patch new file mode 100644 index 0000000000..492a9d3ca9 --- /dev/null +++ b/patches.fixes/ext4-Properly-align-shifted-xattrs-when-expanding-in.patch @@ -0,0 +1,30 @@ +From 06ae7dd7eaf86e2ce558b850d5633cc286bd9c8f Mon Sep 17 00:00:00 2001 +From: Jan Kara <jack@suse.cz> +Date: Thu, 28 Jul 2016 17:00:58 +0200 +Subject: [PATCH 03/11] ext4: Properly align shifted xattrs when expanding + inodes +Patch-mainline: Submitted, Aug 3 2016 +References: bsc#991671 + +We did not count with the padding of xattr value when computing desired +shift of xattrs in the inode when expanding i_extra_isize. As a result +we could create unaligned start of inline xattrs. Account for alignment +properly. + +CC: <stable@vger.kernel.org> # 4.4.x- +Signed-off-by: Jan Kara <jack@suse.cz> +--- + fs/ext4/xattr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -1494,7 +1494,7 @@ retry: + if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff) + shift_bytes = isize_diff; + else +- shift_bytes = entry_size + size; ++ shift_bytes = entry_size + EXT4_XATTR_SIZE(size); + /* Adjust the offsets and shift the remaining entries ahead */ + ext4_xattr_shift_entries(entry, -shift_bytes, + (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE + diff --git a/series.conf b/series.conf index 3550653612..d895d3efcb 100644 --- a/series.conf +++ b/series.conf @@ -1751,6 +1751,10 @@ patches.drivers/ext4-move-trans-handling-and-completion-deferal-out-of-ext4_get_block.patch patches.fixes/ext4-cleanup-handling-of-bh-b_state-in-DAX-mmap.patch + patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes.patch + patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes-2.patch + patches.fixes/ext4-Properly-align-shifted-xattrs-when-expanding-in.patch + patches.fixes/ext4-Avoid-deadlock-when-expanding-inode-size.patch ######################################################## # btrfs |