Home Home > GIT Browse > openSUSE-42.3
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Marek <mmarek@suse.com>2016-08-08 16:24:26 +0200
committerMichal Marek <mmarek@suse.com>2016-08-08 16:24:26 +0200
commit5b281a8ed597129e66345f69780d4efcb9e62f0b (patch)
tree972c1ee1c99ab55b52b60e7026f91fc9e5e925eb
parenta3843a967c6f0e538157a88d66ea906e1bb136df (diff)
parentbfd4ccc2339dac2c14208c52c6f89b383ed920b6 (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).
-rw-r--r--patches.fixes/ext4-Avoid-deadlock-when-expanding-inode-size.patch86
-rw-r--r--patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes-2.patch44
-rw-r--r--patches.fixes/ext4-Fix-xattr-shifting-when-expanding-inodes.patch111
-rw-r--r--patches.fixes/ext4-Properly-align-shifted-xattrs-when-expanding-in.patch30
-rw-r--r--series.conf4
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