]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Nov 2020 15:14:30 +0000 (16:14 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Nov 2020 15:14:30 +0000 (16:14 +0100)
added patches:
btrfs-adjust-return-values-of-btrfs_inode_by_name.patch
btrfs-inode-verify-inode-mode-to-avoid-null-pointer-dereference.patch
btrfs-tree-checker-enhance-chunk-checker-to-validate-chunk-profile.patch

queue-4.14/btrfs-adjust-return-values-of-btrfs_inode_by_name.patch [new file with mode: 0644]
queue-4.14/btrfs-inode-verify-inode-mode-to-avoid-null-pointer-dereference.patch [new file with mode: 0644]
queue-4.14/btrfs-tree-checker-enhance-chunk-checker-to-validate-chunk-profile.patch [new file with mode: 0644]
queue-4.14/series

diff --git a/queue-4.14/btrfs-adjust-return-values-of-btrfs_inode_by_name.patch b/queue-4.14/btrfs-adjust-return-values-of-btrfs_inode_by_name.patch
new file mode 100644 (file)
index 0000000..12cc8ff
--- /dev/null
@@ -0,0 +1,84 @@
+From foo@baz Fri Nov 27 04:14:13 PM CET 2020
+From: Su Yue <suy.fnst@cn.fujitsu.com>
+Date: Mon, 5 Mar 2018 17:13:37 +0800
+Subject: btrfs: adjust return values of btrfs_inode_by_name
+
+From: Su Yue <suy.fnst@cn.fujitsu.com>
+
+commit 005d67127fa9dfb3382f2c9e918feed7a243a7fe upstream
+
+Previously, btrfs_inode_by_name() returned 0 which left caller to check
+objectid of location even location if the type was invalid.
+
+Let btrfs_inode_by_name() return -EUCLEAN if a corrupted location of a
+dir entry is found.  Removal of label out_err also simplifies the
+function.
+
+Signed-off-by: Su Yue <suy.fnst@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+[ drop unlikely ]
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/inode.c |   22 ++++++++++------------
+ 1 file changed, 10 insertions(+), 12 deletions(-)
+
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -5585,7 +5585,8 @@ no_delete:
+ /*
+  * this returns the key found in the dir entry in the location pointer.
+- * If no dir entries were found, location->objectid is 0.
++ * If no dir entries were found, returns -ENOENT.
++ * If found a corrupted location in dir entry, returns -EUCLEAN.
+  */
+ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+                              struct btrfs_key *location)
+@@ -5603,27 +5604,27 @@ static int btrfs_inode_by_name(struct in
+       di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)),
+                       name, namelen, 0);
+-      if (IS_ERR(di))
++      if (!di) {
++              ret = -ENOENT;
++              goto out;
++      }
++      if (IS_ERR(di)) {
+               ret = PTR_ERR(di);
+-
+-      if (IS_ERR_OR_NULL(di))
+-              goto out_err;
++              goto out;
++      }
+       btrfs_dir_item_key_to_cpu(path->nodes[0], di, location);
+       if (location->type != BTRFS_INODE_ITEM_KEY &&
+           location->type != BTRFS_ROOT_ITEM_KEY) {
++              ret = -EUCLEAN;
+               btrfs_warn(root->fs_info,
+ "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
+                          __func__, name, btrfs_ino(BTRFS_I(dir)),
+                          location->objectid, location->type, location->offset);
+-              goto out_err;
+       }
+ out:
+       btrfs_free_path(path);
+       return ret;
+-out_err:
+-      location->objectid = 0;
+-      goto out;
+ }
+ /*
+@@ -5924,9 +5925,6 @@ struct inode *btrfs_lookup_dentry(struct
+       if (ret < 0)
+               return ERR_PTR(ret);
+-      if (location.objectid == 0)
+-              return ERR_PTR(-ENOENT);
+-
+       if (location.type == BTRFS_INODE_ITEM_KEY) {
+               inode = btrfs_iget(dir->i_sb, &location, root, NULL);
+               return inode;
diff --git a/queue-4.14/btrfs-inode-verify-inode-mode-to-avoid-null-pointer-dereference.patch b/queue-4.14/btrfs-inode-verify-inode-mode-to-avoid-null-pointer-dereference.patch
new file mode 100644 (file)
index 0000000..89c216e
--- /dev/null
@@ -0,0 +1,196 @@
+From foo@baz Fri Nov 27 04:14:13 PM CET 2020
+From: Qu Wenruo <wqu@suse.com>
+Date: Wed, 13 Mar 2019 13:55:11 +0800
+Subject: btrfs: inode: Verify inode mode to avoid NULL pointer dereference
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 6bf9e4bd6a277840d3fe8c5d5d530a1fbd3db592 upstream
+
+[BUG]
+When accessing a file on a crafted image, btrfs can crash in block layer:
+
+  BUG: unable to handle kernel NULL pointer dereference at 0000000000000008
+  PGD 136501067 P4D 136501067 PUD 124519067 PMD 0
+  CPU: 3 PID: 0 Comm: swapper/3 Not tainted 5.0.0-rc8-default #252
+  RIP: 0010:end_bio_extent_readpage+0x144/0x700
+  Call Trace:
+   <IRQ>
+   blk_update_request+0x8f/0x350
+   blk_mq_end_request+0x1a/0x120
+   blk_done_softirq+0x99/0xc0
+   __do_softirq+0xc7/0x467
+   irq_exit+0xd1/0xe0
+   call_function_single_interrupt+0xf/0x20
+   </IRQ>
+  RIP: 0010:default_idle+0x1e/0x170
+
+[CAUSE]
+The crafted image has a tricky corruption, the INODE_ITEM has a
+different type against its parent dir:
+
+        item 20 key (268 INODE_ITEM 0) itemoff 2808 itemsize 160
+                generation 13 transid 13 size 1048576 nbytes 1048576
+                block group 0 mode 121644 links 1 uid 0 gid 0 rdev 0
+                sequence 9 flags 0x0(none)
+
+This mode number 0120000 means it's a symlink.
+
+But the dir item think it's still a regular file:
+
+        item 8 key (264 DIR_INDEX 5) itemoff 3707 itemsize 32
+                location key (268 INODE_ITEM 0) type FILE
+                transid 13 data_len 0 name_len 2
+                name: f4
+        item 40 key (264 DIR_ITEM 51821248) itemoff 1573 itemsize 32
+                location key (268 INODE_ITEM 0) type FILE
+                transid 13 data_len 0 name_len 2
+                name: f4
+
+For symlink, we don't set BTRFS_I(inode)->io_tree.ops and leave it
+empty, as symlink is only designed to have inlined extent, all handled
+by tree block read.  Thus no need to trigger btrfs_submit_bio_hook() for
+inline file extent.
+
+However end_bio_extent_readpage() expects tree->ops populated, as it's
+reading regular data extent.  This causes NULL pointer dereference.
+
+[FIX]
+This patch fixes the problem in two ways:
+
+- Verify inode mode against its dir item when looking up inode
+  So in btrfs_lookup_dentry() if we find inode mode mismatch with dir
+  item, we error out so that corrupted inode will not be accessed.
+
+- Verify inode mode when getting extent mapping
+  Only regular file should have regular or preallocated extent.
+  If we found regular/preallocated file extent for symlink or
+  the rest, we error out before submitting the read bio.
+
+With this fix that crafted image can be rejected gracefully:
+
+  BTRFS critical (device loop0): inode mode mismatch with dir: inode mode=0121644 btrfs type=7 dir type=1
+
+Reported-by: Yoon Jungyeon <jungyeon@gatech.edu>
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=202763
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[sudip: use original btrfs_inode_type()]
+Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/inode.c             |   41 +++++++++++++++++++++++++++++++++--------
+ fs/btrfs/tests/inode-tests.c |    1 +
+ 2 files changed, 34 insertions(+), 8 deletions(-)
+
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -5584,12 +5584,14 @@ no_delete:
+ }
+ /*
+- * this returns the key found in the dir entry in the location pointer.
++ * Return the key found in the dir entry in the location pointer, fill @type
++ * with BTRFS_FT_*, and return 0.
++ *
+  * If no dir entries were found, returns -ENOENT.
+  * If found a corrupted location in dir entry, returns -EUCLEAN.
+  */
+ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+-                             struct btrfs_key *location)
++                             struct btrfs_key *location, u8 *type)
+ {
+       const char *name = dentry->d_name.name;
+       int namelen = dentry->d_name.len;
+@@ -5622,6 +5624,8 @@ static int btrfs_inode_by_name(struct in
+                          __func__, name, btrfs_ino(BTRFS_I(dir)),
+                          location->objectid, location->type, location->offset);
+       }
++      if (!ret)
++              *type = btrfs_dir_type(path->nodes[0], di);
+ out:
+       btrfs_free_path(path);
+       return ret;
+@@ -5908,6 +5912,11 @@ static struct inode *new_simple_dir(stru
+       return inode;
+ }
++static inline u8 btrfs_inode_type(struct inode *inode)
++{
++      return btrfs_type_by_mode[(inode->i_mode & S_IFMT) >> S_SHIFT];
++}
++
+ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
+ {
+       struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
+@@ -5915,18 +5924,31 @@ struct inode *btrfs_lookup_dentry(struct
+       struct btrfs_root *root = BTRFS_I(dir)->root;
+       struct btrfs_root *sub_root = root;
+       struct btrfs_key location;
++      u8 di_type = 0;
+       int index;
+       int ret = 0;
+       if (dentry->d_name.len > BTRFS_NAME_LEN)
+               return ERR_PTR(-ENAMETOOLONG);
+-      ret = btrfs_inode_by_name(dir, dentry, &location);
++      ret = btrfs_inode_by_name(dir, dentry, &location, &di_type);
+       if (ret < 0)
+               return ERR_PTR(ret);
+       if (location.type == BTRFS_INODE_ITEM_KEY) {
+               inode = btrfs_iget(dir->i_sb, &location, root, NULL);
++              if (IS_ERR(inode))
++                      return inode;
++
++              /* Do extra check against inode mode with di_type */
++              if (btrfs_inode_type(inode) != di_type) {
++                      btrfs_crit(fs_info,
++"inode mode mismatch with dir: inode mode=0%o btrfs type=%u dir type=%u",
++                                inode->i_mode, btrfs_inode_type(inode),
++                                di_type);
++                      iput(inode);
++                      return ERR_PTR(-EUCLEAN);
++              }
+               return inode;
+       }
+@@ -6544,11 +6566,6 @@ fail:
+       return ERR_PTR(ret);
+ }
+-static inline u8 btrfs_inode_type(struct inode *inode)
+-{
+-      return btrfs_type_by_mode[(inode->i_mode & S_IFMT) >> S_SHIFT];
+-}
+-
+ /*
+  * utility function to add 'inode' into 'parent_inode' with
+  * a give name and a given sequence number.
+@@ -7160,6 +7177,14 @@ again:
+       extent_start = found_key.offset;
+       if (found_type == BTRFS_FILE_EXTENT_REG ||
+           found_type == BTRFS_FILE_EXTENT_PREALLOC) {
++              /* Only regular file could have regular/prealloc extent */
++              if (!S_ISREG(inode->vfs_inode.i_mode)) {
++                      ret = -EUCLEAN;
++                      btrfs_crit(fs_info,
++              "regular/prealloc extent found for non-regular inode %llu",
++                                 btrfs_ino(inode));
++                      goto out;
++              }
+               extent_end = extent_start +
+                      btrfs_file_extent_num_bytes(leaf, item);
+--- a/fs/btrfs/tests/inode-tests.c
++++ b/fs/btrfs/tests/inode-tests.c
+@@ -245,6 +245,7 @@ static noinline int test_btrfs_get_exten
+               return ret;
+       }
++      inode->i_mode = S_IFREG;
+       BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
+       BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID;
+       BTRFS_I(inode)->location.offset = 0;
diff --git a/queue-4.14/btrfs-tree-checker-enhance-chunk-checker-to-validate-chunk-profile.patch b/queue-4.14/btrfs-tree-checker-enhance-chunk-checker-to-validate-chunk-profile.patch
new file mode 100644 (file)
index 0000000..41aa6bd
--- /dev/null
@@ -0,0 +1,45 @@
+From foo@baz Fri Nov 27 04:14:13 PM CET 2020
+From: Qu Wenruo <wqu@suse.com>
+Date: Wed, 13 Mar 2019 12:17:50 +0800
+Subject: btrfs: tree-checker: Enhance chunk checker to validate chunk profile
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 80e46cf22ba0bcb57b39c7c3b52961ab3a0fd5f2 upstream
+
+Btrfs-progs already have a comprehensive type checker, to ensure there
+is only 0 (SINGLE profile) or 1 (DUP/RAID0/1/5/6/10) bit set for chunk
+profile bits.
+
+Do the same work for kernel.
+
+Reported-by: Yoon Jungyeon <jungyeon@gatech.edu>
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=202765
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[sudip: manually backport and use btrfs_err instead of chunk_err]
+Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/volumes.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -6406,6 +6406,13 @@ static int btrfs_check_chunk_valid(struc
+               return -EIO;
+       }
++      if (!is_power_of_2(type & BTRFS_BLOCK_GROUP_PROFILE_MASK) &&
++          (type & BTRFS_BLOCK_GROUP_PROFILE_MASK) != 0) {
++              btrfs_err(fs_info,
++              "invalid chunk profile flag: 0x%llx, expect 0 or 1 bit set",
++                        type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
++              return -EUCLEAN;
++      }
+       if ((type & BTRFS_BLOCK_GROUP_TYPE_MASK) == 0) {
+               btrfs_err(fs_info, "missing chunk type flag: 0x%llx", type);
+               return -EIO;
index 4b1329bd2ece346a1142439bea48922152536801..813b01a4774beba422409e61ea9fd85078b21cc1 100644 (file)
@@ -2,3 +2,6 @@ perf-event-check-ref_reloc_sym-before-using-it.patch
 mm-userfaultfd-do-not-access-vma-vm_mm-after-calling-handle_userfault.patch
 wireless-use-linux-stddef.h-instead-of-stddef.h.patch
 pci-add-device-even-if-driver-attach-failed.patch
+btrfs-tree-checker-enhance-chunk-checker-to-validate-chunk-profile.patch
+btrfs-adjust-return-values-of-btrfs_inode_by_name.patch
+btrfs-inode-verify-inode-mode-to-avoid-null-pointer-dereference.patch