]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 14 Jan 2019 13:49:58 +0000 (14:49 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 14 Jan 2019 13:49:58 +0000 (14:49 +0100)
added patches:
btrfs-add-checker-for-extent_csum.patch
btrfs-add-sanity-check-for-extent_data-when-reading-out-leaf.patch
btrfs-add-validadtion-checks-for-chunk-loading.patch
btrfs-check-btree-node-s-nritems.patch
btrfs-check-if-item-pointer-overlaps-with-the-item-itself.patch
btrfs-check-inconsistence-between-chunk-and-block-group.patch
btrfs-check-that-each-block-group-has-corresponding-chunk-at-mount-time.patch
btrfs-cleanup-stop-casting-for-extent_map-lookup-everywhere.patch
btrfs-detect-corruption-when-non-root-leaf-has-zero-item.patch
btrfs-enhance-chunk-validation-check.patch
btrfs-fix-bug_on-in-btrfs_mark_buffer_dirty.patch
btrfs-fix-em-leak-in-find_first_block_group.patch
btrfs-fix-emptiness-check-for-dirtied-extent-buffers-at-check_leaf.patch
btrfs-improve-check_node-to-avoid-reading-corrupted-nodes.patch
btrfs-kill-bug_on-in-run_delayed_tree_ref.patch
btrfs-memset-to-avoid-stale-content-in-btree-leaf.patch
btrfs-memset-to-avoid-stale-content-in-btree-node-block.patch
btrfs-move-leaf-and-node-validation-checker-to-tree-checker.c.patch
btrfs-refactor-check_leaf-function-for-later-expansion.patch
btrfs-struct-funcs-constify-readers.patch
btrfs-tree-check-reduce-stack-consumption-in-check_dir_item.patch
btrfs-tree-checker-add-checker-for-dir-item.patch
btrfs-tree-checker-check-level-for-leaves-and-nodes.patch
btrfs-tree-checker-detect-invalid-and-empty-essential-trees.patch
btrfs-tree-checker-enhance-btrfs_check_node-output.patch
btrfs-tree-checker-fix-false-panic-for-sanity-test.patch
btrfs-tree-checker-fix-misleading-group-system-information.patch
btrfs-tree-checker-use-zu-format-string-for-size_t.patch
btrfs-tree-checker-verify-block_group_item.patch
btrfs-validate-type-when-reading-a-chunk.patch
btrfs-verify-that-every-chunk-has-corresponding-block-group-at-mount-time.patch

32 files changed:
queue-4.4/btrfs-add-checker-for-extent_csum.patch [new file with mode: 0644]
queue-4.4/btrfs-add-sanity-check-for-extent_data-when-reading-out-leaf.patch [new file with mode: 0644]
queue-4.4/btrfs-add-validadtion-checks-for-chunk-loading.patch [new file with mode: 0644]
queue-4.4/btrfs-check-btree-node-s-nritems.patch [new file with mode: 0644]
queue-4.4/btrfs-check-if-item-pointer-overlaps-with-the-item-itself.patch [new file with mode: 0644]
queue-4.4/btrfs-check-inconsistence-between-chunk-and-block-group.patch [new file with mode: 0644]
queue-4.4/btrfs-check-that-each-block-group-has-corresponding-chunk-at-mount-time.patch [new file with mode: 0644]
queue-4.4/btrfs-cleanup-stop-casting-for-extent_map-lookup-everywhere.patch [new file with mode: 0644]
queue-4.4/btrfs-detect-corruption-when-non-root-leaf-has-zero-item.patch [new file with mode: 0644]
queue-4.4/btrfs-enhance-chunk-validation-check.patch [new file with mode: 0644]
queue-4.4/btrfs-fix-bug_on-in-btrfs_mark_buffer_dirty.patch [new file with mode: 0644]
queue-4.4/btrfs-fix-em-leak-in-find_first_block_group.patch [new file with mode: 0644]
queue-4.4/btrfs-fix-emptiness-check-for-dirtied-extent-buffers-at-check_leaf.patch [new file with mode: 0644]
queue-4.4/btrfs-improve-check_node-to-avoid-reading-corrupted-nodes.patch [new file with mode: 0644]
queue-4.4/btrfs-kill-bug_on-in-run_delayed_tree_ref.patch [new file with mode: 0644]
queue-4.4/btrfs-memset-to-avoid-stale-content-in-btree-leaf.patch [new file with mode: 0644]
queue-4.4/btrfs-memset-to-avoid-stale-content-in-btree-node-block.patch [new file with mode: 0644]
queue-4.4/btrfs-move-leaf-and-node-validation-checker-to-tree-checker.c.patch [new file with mode: 0644]
queue-4.4/btrfs-refactor-check_leaf-function-for-later-expansion.patch [new file with mode: 0644]
queue-4.4/btrfs-struct-funcs-constify-readers.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-check-reduce-stack-consumption-in-check_dir_item.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-add-checker-for-dir-item.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-check-level-for-leaves-and-nodes.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-detect-invalid-and-empty-essential-trees.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-enhance-btrfs_check_node-output.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-fix-false-panic-for-sanity-test.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-fix-misleading-group-system-information.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-use-zu-format-string-for-size_t.patch [new file with mode: 0644]
queue-4.4/btrfs-tree-checker-verify-block_group_item.patch [new file with mode: 0644]
queue-4.4/btrfs-validate-type-when-reading-a-chunk.patch [new file with mode: 0644]
queue-4.4/btrfs-verify-that-every-chunk-has-corresponding-block-group-at-mount-time.patch [new file with mode: 0644]
queue-4.4/series

diff --git a/queue-4.4/btrfs-add-checker-for-extent_csum.patch b/queue-4.4/btrfs-add-checker-for-extent_csum.patch
new file mode 100644 (file)
index 0000000..fb84d3b
--- /dev/null
@@ -0,0 +1,72 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Date: Wed, 23 Aug 2017 16:57:59 +0900
+Subject: btrfs: Add checker for EXTENT_CSUM
+
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+
+commit 4b865cab96fe2a30ed512cf667b354bd291b3b0a upstream.
+
+EXTENT_CSUM checker is a relatively easy one, only needs to check:
+
+1) Objectid
+   Fixed to BTRFS_EXTENT_CSUM_OBJECTID
+
+2) Key offset alignment
+   Must be aligned to sectorsize
+
+3) Item size alignedment
+   Must be aligned to csum size
+
+Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: Use root->sectorsize instead of
+ root->fs_info->sectorsize]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -605,6 +605,27 @@ static int check_extent_data_item(struct
+       return 0;
+ }
++static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,
++                         struct btrfs_key *key, int slot)
++{
++      u32 sectorsize = root->sectorsize;
++      u32 csumsize = btrfs_super_csum_size(root->fs_info->super_copy);
++
++      if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) {
++              CORRUPT("invalid objectid for csum item", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (!IS_ALIGNED(key->offset, sectorsize)) {
++              CORRUPT("unaligned key offset for csum item", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) {
++              CORRUPT("unaligned csum item size", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      return 0;
++}
++
+ /*
+  * Common point to switch the item-specific validation.
+  */
+@@ -618,6 +639,9 @@ static int check_leaf_item(struct btrfs_
+       case BTRFS_EXTENT_DATA_KEY:
+               ret = check_extent_data_item(root, leaf, key, slot);
+               break;
++      case BTRFS_EXTENT_CSUM_KEY:
++              ret = check_csum_item(root, leaf, key, slot);
++              break;
+       }
+       return ret;
+ }
diff --git a/queue-4.4/btrfs-add-sanity-check-for-extent_data-when-reading-out-leaf.patch b/queue-4.4/btrfs-add-sanity-check-for-extent_data-when-reading-out-leaf.patch
new file mode 100644 (file)
index 0000000..cf520a9
--- /dev/null
@@ -0,0 +1,185 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Date: Wed, 23 Aug 2017 16:57:58 +0900
+Subject: btrfs: Add sanity check for EXTENT_DATA when reading out leaf
+
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+
+commit 40c3c40947324d9f40bf47830c92c59a9bbadf4a upstream.
+
+Add extra checks for item with EXTENT_DATA type.  This checks the
+following thing:
+
+0) Key offset
+   All key offsets must be aligned to sectorsize.
+   Inline extent must have 0 for key offset.
+
+1) Item size
+   Uncompressed inline file extent size must match item size.
+   (Compressed inline file extent has no information about its on-disk size.)
+   Regular/preallocated file extent size must be a fixed value.
+
+2) Every member of regular file extent item
+   Including alignment for bytenr and offset, possible value for
+   compression/encryption/type.
+
+3) Type/compression/encode must be one of the valid values.
+
+This should be the most comprehensive and strict check in the context
+of btrfs_item for EXTENT_DATA.
+
+Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+[ switch to BTRFS_FILE_EXTENT_TYPES, similar to what
+  BTRFS_COMPRESS_TYPES does ]
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4:
+ - Use root->sectorsize instead of root->fs_info->sectorsize
+ - Adjust filename]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/ctree.h   |    1 
+ fs/btrfs/disk-io.c |  103 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 104 insertions(+)
+
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -897,6 +897,7 @@ struct btrfs_balance_item {
+ #define BTRFS_FILE_EXTENT_INLINE 0
+ #define BTRFS_FILE_EXTENT_REG 1
+ #define BTRFS_FILE_EXTENT_PREALLOC 2
++#define BTRFS_FILE_EXTENT_TYPES       2
+ struct btrfs_file_extent_item {
+       /*
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -528,6 +528,100 @@ static int check_tree_block_fsid(struct
+                  btrfs_header_level(eb) == 0 ? "leaf" : "node",\
+                  reason, btrfs_header_bytenr(eb), root->objectid, slot)
++static int check_extent_data_item(struct btrfs_root *root,
++                                struct extent_buffer *leaf,
++                                struct btrfs_key *key, int slot)
++{
++      struct btrfs_file_extent_item *fi;
++      u32 sectorsize = root->sectorsize;
++      u32 item_size = btrfs_item_size_nr(leaf, slot);
++
++      if (!IS_ALIGNED(key->offset, sectorsize)) {
++              CORRUPT("unaligned key offset for file extent",
++                      leaf, root, slot);
++              return -EUCLEAN;
++      }
++
++      fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
++
++      if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) {
++              CORRUPT("invalid file extent type", leaf, root, slot);
++              return -EUCLEAN;
++      }
++
++      /*
++       * Support for new compression/encrption must introduce incompat flag,
++       * and must be caught in open_ctree().
++       */
++      if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) {
++              CORRUPT("invalid file extent compression", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (btrfs_file_extent_encryption(leaf, fi)) {
++              CORRUPT("invalid file extent encryption", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
++              /* Inline extent must have 0 as key offset */
++              if (key->offset) {
++                      CORRUPT("inline extent has non-zero key offset",
++                              leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
++              /* Compressed inline extent has no on-disk size, skip it */
++              if (btrfs_file_extent_compression(leaf, fi) !=
++                  BTRFS_COMPRESS_NONE)
++                      return 0;
++
++              /* Uncompressed inline extent size must match item size */
++              if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START +
++                  btrfs_file_extent_ram_bytes(leaf, fi)) {
++                      CORRUPT("plaintext inline extent has invalid size",
++                              leaf, root, slot);
++                      return -EUCLEAN;
++              }
++              return 0;
++      }
++
++      /* Regular or preallocated extent has fixed item size */
++      if (item_size != sizeof(*fi)) {
++              CORRUPT(
++              "regluar or preallocated extent data item size is invalid",
++                      leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) {
++              CORRUPT(
++              "regular or preallocated extent data item has unaligned value",
++                      leaf, root, slot);
++              return -EUCLEAN;
++      }
++
++      return 0;
++}
++
++/*
++ * Common point to switch the item-specific validation.
++ */
++static int check_leaf_item(struct btrfs_root *root,
++                         struct extent_buffer *leaf,
++                         struct btrfs_key *key, int slot)
++{
++      int ret = 0;
++
++      switch (key->type) {
++      case BTRFS_EXTENT_DATA_KEY:
++              ret = check_extent_data_item(root, leaf, key, slot);
++              break;
++      }
++      return ret;
++}
++
+ static noinline int check_leaf(struct btrfs_root *root,
+                              struct extent_buffer *leaf)
+ {
+@@ -583,9 +677,13 @@ static noinline int check_leaf(struct bt
+        * 1) key order
+        * 2) item offset and size
+        *    No overlap, no hole, all inside the leaf.
++       * 3) item content
++       *    If possible, do comprehensive sanity check.
++       *    NOTE: All checks must only rely on the item data itself.
+        */
+       for (slot = 0; slot < nritems; slot++) {
+               u32 item_end_expected;
++              int ret;
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+@@ -628,6 +726,11 @@ static noinline int check_leaf(struct bt
+                       return -EUCLEAN;
+               }
++              /* Check if the item size and content meet other criteria */
++              ret = check_leaf_item(root, leaf, &key, slot);
++              if (ret < 0)
++                      return ret;
++
+               prev_key.objectid = key.objectid;
+               prev_key.type = key.type;
+               prev_key.offset = key.offset;
diff --git a/queue-4.4/btrfs-add-validadtion-checks-for-chunk-loading.patch b/queue-4.4/btrfs-add-validadtion-checks-for-chunk-loading.patch
new file mode 100644 (file)
index 0000000..60b0291
--- /dev/null
@@ -0,0 +1,160 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Fri, 3 Jun 2016 12:05:15 -0700
+Subject: Btrfs: add validadtion checks for chunk loading
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit e06cd3dd7cea50e87663a88acdfdb7ac1c53a5ca upstream.
+
+To prevent fuzzed filesystem images from panic the whole system,
+we need various validation checks to refuse to mount such an image
+if btrfs finds any invalid value during loading chunks, including
+both sys_array and regular chunks.
+
+Note that these checks may not be sufficient to cover all corner cases,
+feel free to add more checks.
+
+Reported-by: Vegard Nossum <vegard.nossum@oracle.com>
+Reported-by: Quentin Casasnovas <quentin.casasnovas@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/volumes.c |   82 +++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 67 insertions(+), 15 deletions(-)
+
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -6208,27 +6208,23 @@ struct btrfs_device *btrfs_alloc_device(
+       return dev;
+ }
+-static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
+-                        struct extent_buffer *leaf,
+-                        struct btrfs_chunk *chunk)
++/* Return -EIO if any error, otherwise return 0. */
++static int btrfs_check_chunk_valid(struct btrfs_root *root,
++                                 struct extent_buffer *leaf,
++                                 struct btrfs_chunk *chunk, u64 logical)
+ {
+-      struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
+-      struct map_lookup *map;
+-      struct extent_map *em;
+-      u64 logical;
+       u64 length;
+       u64 stripe_len;
+-      u64 devid;
+-      u8 uuid[BTRFS_UUID_SIZE];
+-      int num_stripes;
+-      int ret;
+-      int i;
++      u16 num_stripes;
++      u16 sub_stripes;
++      u64 type;
+-      logical = key->offset;
+       length = btrfs_chunk_length(leaf, chunk);
+       stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+       num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+-      /* Validation check */
++      sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
++      type = btrfs_chunk_type(leaf, chunk);
++
+       if (!num_stripes) {
+               btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
+                         num_stripes);
+@@ -6239,6 +6235,11 @@ static int read_one_chunk(struct btrfs_r
+                         "invalid chunk logical %llu", logical);
+               return -EIO;
+       }
++      if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
++              btrfs_err(root->fs_info, "invalid chunk sectorsize %u",
++                        btrfs_chunk_sector_size(leaf, chunk));
++              return -EIO;
++      }
+       if (!length || !IS_ALIGNED(length, root->sectorsize)) {
+               btrfs_err(root->fs_info,
+                       "invalid chunk length %llu", length);
+@@ -6250,13 +6251,54 @@ static int read_one_chunk(struct btrfs_r
+               return -EIO;
+       }
+       if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
+-          btrfs_chunk_type(leaf, chunk)) {
++          type) {
+               btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
+                         ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+                           BTRFS_BLOCK_GROUP_PROFILE_MASK) &
+                         btrfs_chunk_type(leaf, chunk));
+               return -EIO;
+       }
++      if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) ||
++          (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
++          (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
++          (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
++          (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
++          ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
++           num_stripes != 1)) {
++              btrfs_err(root->fs_info,
++                      "invalid num_stripes:sub_stripes %u:%u for profile %llu",
++                      num_stripes, sub_stripes,
++                      type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
++              return -EIO;
++      }
++
++      return 0;
++}
++
++static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
++                        struct extent_buffer *leaf,
++                        struct btrfs_chunk *chunk)
++{
++      struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
++      struct map_lookup *map;
++      struct extent_map *em;
++      u64 logical;
++      u64 length;
++      u64 stripe_len;
++      u64 devid;
++      u8 uuid[BTRFS_UUID_SIZE];
++      int num_stripes;
++      int ret;
++      int i;
++
++      logical = key->offset;
++      length = btrfs_chunk_length(leaf, chunk);
++      stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
++      num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
++
++      ret = btrfs_check_chunk_valid(root, leaf, chunk, logical);
++      if (ret)
++              return ret;
+       read_lock(&map_tree->map_tree.lock);
+       em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
+@@ -6504,6 +6546,7 @@ int btrfs_read_sys_array(struct btrfs_ro
+       u32 array_size;
+       u32 len = 0;
+       u32 cur_offset;
++      u64 type;
+       struct btrfs_key key;
+       ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize);
+@@ -6569,6 +6612,15 @@ int btrfs_read_sys_array(struct btrfs_ro
+                               ret = -EIO;
+                               break;
+                       }
++
++                      type = btrfs_chunk_type(sb, chunk);
++                      if ((type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
++                              btrfs_err(root->fs_info,
++                          "invalid chunk type %llu in sys_array at offset %u",
++                                      type, cur_offset);
++                              ret = -EIO;
++                              break;
++                      }
+                       len = btrfs_chunk_item_size(num_stripes);
+                       if (cur_offset + len > array_size)
diff --git a/queue-4.4/btrfs-check-btree-node-s-nritems.patch b/queue-4.4/btrfs-check-btree-node-s-nritems.patch
new file mode 100644 (file)
index 0000000..00a695b
--- /dev/null
@@ -0,0 +1,63 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Tue, 23 Aug 2016 17:37:45 -0700
+Subject: Btrfs: check btree node's nritems
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 053ab70f0604224c7893b43f9d9d5efa283580d6 upstream.
+
+When btree node (level = 1) has nritems which equals to zero,
+we can end up with panic due to insert_ptr()'s
+
+BUG_ON(slot > nritems);
+
+where slot is 1 and nritems is 0, as copy_for_split() calls
+insert_ptr(.., path->slots[1] + 1, ...);
+
+A invalid value results in the whole mess, this adds the check
+for btree's node nritems so that we stop reading block when
+when something is wrong.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Chris Mason <clm@fb.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -609,6 +609,19 @@ static noinline int check_leaf(struct bt
+       return 0;
+ }
++static int check_node(struct btrfs_root *root, struct extent_buffer *node)
++{
++      unsigned long nr = btrfs_header_nritems(node);
++
++      if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
++              btrfs_crit(root->fs_info,
++                         "corrupt node: block %llu root %llu nritems %lu",
++                         node->start, root->objectid, nr);
++              return -EIO;
++      }
++      return 0;
++}
++
+ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
+                                     u64 phy_offset, struct page *page,
+                                     u64 start, u64 end, int mirror)
+@@ -680,6 +693,9 @@ static int btree_readpage_end_io_hook(st
+               ret = -EIO;
+       }
++      if (found_level > 0 && check_node(root, eb))
++              ret = -EIO;
++
+       if (!ret)
+               set_extent_buffer_uptodate(eb);
+ err:
diff --git a/queue-4.4/btrfs-check-if-item-pointer-overlaps-with-the-item-itself.patch b/queue-4.4/btrfs-check-if-item-pointer-overlaps-with-the-item-itself.patch
new file mode 100644 (file)
index 0000000..10958ff
--- /dev/null
@@ -0,0 +1,41 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Date: Wed, 23 Aug 2017 16:57:57 +0900
+Subject: btrfs: Check if item pointer overlaps with the item itself
+
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+
+commit 7f43d4affb2a254d421ab20b0cf65ac2569909fb upstream.
+
+Function check_leaf() checks if any item pointer points outside of the
+leaf, but it doesn't check if the pointer overlaps with the item itself.
+
+Normally only the last item may be the victim, but adding such check is
+never a bad idea anyway.
+
+Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -621,6 +621,13 @@ static noinline int check_leaf(struct bt
+                       return -EUCLEAN;
+               }
++              /* Also check if the item pointer overlaps with btrfs item. */
++              if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) >
++                  btrfs_item_ptr_offset(leaf, slot)) {
++                      CORRUPT("slot overlap with its data", leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
+               prev_key.objectid = key.objectid;
+               prev_key.type = key.type;
+               prev_key.offset = key.offset;
diff --git a/queue-4.4/btrfs-check-inconsistence-between-chunk-and-block-group.patch b/queue-4.4/btrfs-check-inconsistence-between-chunk-and-block-group.patch
new file mode 100644 (file)
index 0000000..8fc7a2e
--- /dev/null
@@ -0,0 +1,50 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Wed, 22 Jun 2016 18:31:27 -0700
+Subject: Btrfs: check inconsistence between chunk and block group
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 6fb37b756acce6d6e045f79c3764206033f617b4 upstream.
+
+With btrfs-corrupt-block, one can drop one chunk item and mounting
+will end up with a panic in btrfs_full_stripe_len().
+
+This doesn't not remove the BUG_ON, but instead checks it a bit
+earlier when we find the block group item.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent-tree.c |   17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -9502,7 +9502,22 @@ static int find_first_block_group(struct
+               if (found_key.objectid >= key->objectid &&
+                   found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) {
+-                      ret = 0;
++                      struct extent_map_tree *em_tree;
++                      struct extent_map *em;
++
++                      em_tree = &root->fs_info->mapping_tree.map_tree;
++                      read_lock(&em_tree->lock);
++                      em = lookup_extent_mapping(em_tree, found_key.objectid,
++                                                 found_key.offset);
++                      read_unlock(&em_tree->lock);
++                      if (!em) {
++                              btrfs_err(root->fs_info,
++                      "logical %llu len %llu found bg but no related chunk",
++                                        found_key.objectid, found_key.offset);
++                              ret = -ENOENT;
++                      } else {
++                              ret = 0;
++                      }
+                       goto out;
+               }
+               path->slots[0]++;
diff --git a/queue-4.4/btrfs-check-that-each-block-group-has-corresponding-chunk-at-mount-time.patch b/queue-4.4/btrfs-check-that-each-block-group-has-corresponding-chunk-at-mount-time.patch
new file mode 100644 (file)
index 0000000..b0a56d6
--- /dev/null
@@ -0,0 +1,83 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Wed, 1 Aug 2018 10:37:16 +0800
+Subject: btrfs: Check that each block group has corresponding chunk at mount time
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 514c7dca85a0bf40be984dab0b477403a6db901f upstream.
+
+A crafted btrfs image with incorrect chunk<->block group mapping will
+trigger a lot of unexpected things as the mapping is essential.
+
+Although the problem can be caught by block group item checker
+added in "btrfs: tree-checker: Verify block_group_item", it's still not
+sufficient.  A sufficiently valid block group item can pass the check
+added by the mentioned patch but could fail to match the existing chunk.
+
+This patch will add extra block group -> chunk mapping check, to ensure
+we have a completely matching (start, len, flags) chunk for each block
+group at mount time.
+
+Here we reuse the original helper find_first_block_group(), which is
+already doing the basic bg -> chunk checks, adding further checks of the
+start/len and type flags.
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=199837
+Reported-by: Xu Wen <wen.xu@gatech.edu>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Su Yue <suy.fnst@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: Use root->fs_info instead of fs_info]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent-tree.c |   28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -9487,6 +9487,8 @@ static int find_first_block_group(struct
+       int ret = 0;
+       struct btrfs_key found_key;
+       struct extent_buffer *leaf;
++      struct btrfs_block_group_item bg;
++      u64 flags;
+       int slot;
+       ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
+@@ -9521,8 +9523,32 @@ static int find_first_block_group(struct
+                       "logical %llu len %llu found bg but no related chunk",
+                                         found_key.objectid, found_key.offset);
+                               ret = -ENOENT;
++                      } else if (em->start != found_key.objectid ||
++                                 em->len != found_key.offset) {
++                              btrfs_err(root->fs_info,
++              "block group %llu len %llu mismatch with chunk %llu len %llu",
++                                        found_key.objectid, found_key.offset,
++                                        em->start, em->len);
++                              ret = -EUCLEAN;
+                       } else {
+-                              ret = 0;
++                              read_extent_buffer(leaf, &bg,
++                                      btrfs_item_ptr_offset(leaf, slot),
++                                      sizeof(bg));
++                              flags = btrfs_block_group_flags(&bg) &
++                                      BTRFS_BLOCK_GROUP_TYPE_MASK;
++
++                              if (flags != (em->map_lookup->type &
++                                            BTRFS_BLOCK_GROUP_TYPE_MASK)) {
++                                      btrfs_err(root->fs_info,
++"block group %llu len %llu type flags 0x%llx mismatch with chunk type flags 0x%llx",
++                                              found_key.objectid,
++                                              found_key.offset, flags,
++                                              (BTRFS_BLOCK_GROUP_TYPE_MASK &
++                                               em->map_lookup->type));
++                                      ret = -EUCLEAN;
++                              } else {
++                                      ret = 0;
++                              }
+                       }
+                       free_extent_map(em);
+                       goto out;
diff --git a/queue-4.4/btrfs-cleanup-stop-casting-for-extent_map-lookup-everywhere.patch b/queue-4.4/btrfs-cleanup-stop-casting-for-extent_map-lookup-everywhere.patch
new file mode 100644 (file)
index 0000000..97ed5cb
--- /dev/null
@@ -0,0 +1,199 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Jeff Mahoney <jeffm@suse.com>
+Date: Wed, 3 Jun 2015 10:55:48 -0400
+Subject: btrfs: cleanup, stop casting for extent_map->lookup everywhere
+
+From: Jeff Mahoney <jeffm@suse.com>
+
+commit 95617d69326ce386c95e33db7aeb832b45ee9f8f upstream.
+
+Overloading extent_map->bdev to struct map_lookup * might have started out
+as a means to an end, but it's a pattern that's used all over the place
+now. Let's get rid of the casting and just add a union instead.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/dev-replace.c |    2 +-
+ fs/btrfs/extent-tree.c |    2 +-
+ fs/btrfs/extent_map.c  |    2 +-
+ fs/btrfs/extent_map.h  |   10 +++++++++-
+ fs/btrfs/scrub.c       |    2 +-
+ fs/btrfs/volumes.c     |   24 ++++++++++++------------
+ 6 files changed, 25 insertions(+), 17 deletions(-)
+
+--- a/fs/btrfs/dev-replace.c
++++ b/fs/btrfs/dev-replace.c
+@@ -620,7 +620,7 @@ static void btrfs_dev_replace_update_dev
+               em = lookup_extent_mapping(em_tree, start, (u64)-1);
+               if (!em)
+                       break;
+-              map = (struct map_lookup *)em->bdev;
++              map = em->map_lookup;
+               for (i = 0; i < map->num_stripes; i++)
+                       if (srcdev == map->stripes[i].dev)
+                               map->stripes[i].dev = tgtdev;
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -10388,7 +10388,7 @@ btrfs_start_trans_remove_block_group(str
+        * more device items and remove one chunk item), but this is done at
+        * btrfs_remove_chunk() through a call to check_system_chunk().
+        */
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       num_items = 3 + map->num_stripes;
+       free_extent_map(em);
+--- a/fs/btrfs/extent_map.c
++++ b/fs/btrfs/extent_map.c
+@@ -76,7 +76,7 @@ void free_extent_map(struct extent_map *
+               WARN_ON(extent_map_in_tree(em));
+               WARN_ON(!list_empty(&em->list));
+               if (test_bit(EXTENT_FLAG_FS_MAPPING, &em->flags))
+-                      kfree(em->bdev);
++                      kfree(em->map_lookup);
+               kmem_cache_free(extent_map_cache, em);
+       }
+ }
+--- a/fs/btrfs/extent_map.h
++++ b/fs/btrfs/extent_map.h
+@@ -32,7 +32,15 @@ struct extent_map {
+       u64 block_len;
+       u64 generation;
+       unsigned long flags;
+-      struct block_device *bdev;
++      union {
++              struct block_device *bdev;
++
++              /*
++               * used for chunk mappings
++               * flags & EXTENT_FLAG_FS_MAPPING must be set
++               */
++              struct map_lookup *map_lookup;
++      };
+       atomic_t refs;
+       unsigned int compress_type;
+       struct list_head list;
+--- a/fs/btrfs/scrub.c
++++ b/fs/btrfs/scrub.c
+@@ -3460,7 +3460,7 @@ static noinline_for_stack int scrub_chun
+               return ret;
+       }
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       if (em->start != chunk_offset)
+               goto out;
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -1184,7 +1184,7 @@ again:
+               struct map_lookup *map;
+               int i;
+-              map = (struct map_lookup *)em->bdev;
++              map = em->map_lookup;
+               for (i = 0; i < map->num_stripes; i++) {
+                       u64 end;
+@@ -2757,7 +2757,7 @@ int btrfs_remove_chunk(struct btrfs_tran
+                       free_extent_map(em);
+               return -EINVAL;
+       }
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       lock_chunks(root->fs_info->chunk_root);
+       check_system_chunk(trans, extent_root, map->type);
+       unlock_chunks(root->fs_info->chunk_root);
+@@ -4731,7 +4731,7 @@ static int __btrfs_alloc_chunk(struct bt
+               goto error;
+       }
+       set_bit(EXTENT_FLAG_FS_MAPPING, &em->flags);
+-      em->bdev = (struct block_device *)map;
++      em->map_lookup = map;
+       em->start = start;
+       em->len = num_bytes;
+       em->block_start = 0;
+@@ -4826,7 +4826,7 @@ int btrfs_finish_chunk_alloc(struct btrf
+               return -EINVAL;
+       }
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       item_size = btrfs_chunk_item_size(map->num_stripes);
+       stripe_size = em->orig_block_len;
+@@ -4968,7 +4968,7 @@ int btrfs_chunk_readonly(struct btrfs_ro
+       if (!em)
+               return 1;
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       for (i = 0; i < map->num_stripes; i++) {
+               if (map->stripes[i].dev->missing) {
+                       miss_ndevs++;
+@@ -5048,7 +5048,7 @@ int btrfs_num_copies(struct btrfs_fs_inf
+               return 1;
+       }
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       if (map->type & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1))
+               ret = map->num_stripes;
+       else if (map->type & BTRFS_BLOCK_GROUP_RAID10)
+@@ -5091,7 +5091,7 @@ unsigned long btrfs_full_stripe_len(stru
+       BUG_ON(!em);
+       BUG_ON(em->start > logical || em->start + em->len < logical);
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
+               len = map->stripe_len * nr_data_stripes(map);
+       free_extent_map(em);
+@@ -5112,7 +5112,7 @@ int btrfs_is_parity_mirror(struct btrfs_
+       BUG_ON(!em);
+       BUG_ON(em->start > logical || em->start + em->len < logical);
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK)
+               ret = 1;
+       free_extent_map(em);
+@@ -5271,7 +5271,7 @@ static int __btrfs_map_block(struct btrf
+               return -EINVAL;
+       }
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       offset = logical - em->start;
+       stripe_len = map->stripe_len;
+@@ -5813,7 +5813,7 @@ int btrfs_rmap_block(struct btrfs_mappin
+               free_extent_map(em);
+               return -EIO;
+       }
+-      map = (struct map_lookup *)em->bdev;
++      map = em->map_lookup;
+       length = em->len;
+       rmap_len = map->stripe_len;
+@@ -6249,7 +6249,7 @@ static int read_one_chunk(struct btrfs_r
+       }
+       set_bit(EXTENT_FLAG_FS_MAPPING, &em->flags);
+-      em->bdev = (struct block_device *)map;
++      em->map_lookup = map;
+       em->start = logical;
+       em->len = length;
+       em->orig_start = 0;
+@@ -6948,7 +6948,7 @@ void btrfs_update_commit_device_bytes_us
+       /* In order to kick the device replace finish process */
+       lock_chunks(root);
+       list_for_each_entry(em, &transaction->pending_chunks, list) {
+-              map = (struct map_lookup *)em->bdev;
++              map = em->map_lookup;
+               for (i = 0; i < map->num_stripes; i++) {
+                       dev = map->stripes[i].dev;
diff --git a/queue-4.4/btrfs-detect-corruption-when-non-root-leaf-has-zero-item.patch b/queue-4.4/btrfs-detect-corruption-when-non-root-leaf-has-zero-item.patch
new file mode 100644 (file)
index 0000000..8e97915
--- /dev/null
@@ -0,0 +1,62 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Tue, 23 Aug 2016 15:22:58 -0700
+Subject: Btrfs: detect corruption when non-root leaf has zero item
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 1ba98d086fe3a14d6a31f2f66dbab70c45d00f63 upstream.
+
+Right now we treat leaf which has zero item as a valid one
+because we could have an empty tree, that is, a root that is
+also a leaf without any item, however, in the same case but
+when the leaf is not a root, we can end up with hitting the
+BUG_ON(1) in btrfs_extend_item() called by
+setup_inline_extent_backref().
+
+This makes us check the situation as a corruption if leaf is
+not its own root.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Chris Mason <clm@fb.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   23 ++++++++++++++++++++++-
+ 1 file changed, 22 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -535,8 +535,29 @@ static noinline int check_leaf(struct bt
+       u32 nritems = btrfs_header_nritems(leaf);
+       int slot;
+-      if (nritems == 0)
++      if (nritems == 0) {
++              struct btrfs_root *check_root;
++
++              key.objectid = btrfs_header_owner(leaf);
++              key.type = BTRFS_ROOT_ITEM_KEY;
++              key.offset = (u64)-1;
++
++              check_root = btrfs_get_fs_root(root->fs_info, &key, false);
++              /*
++               * The only reason we also check NULL here is that during
++               * open_ctree() some roots has not yet been set up.
++               */
++              if (!IS_ERR_OR_NULL(check_root)) {
++                      /* if leaf is the root, then it's fine */
++                      if (leaf->start !=
++                          btrfs_root_bytenr(&check_root->root_item)) {
++                              CORRUPT("non-root leaf's nritems is 0",
++                                      leaf, root, 0);
++                              return -EIO;
++                      }
++              }
+               return 0;
++      }
+       /* Check the 0 item */
+       if (btrfs_item_offset_nr(leaf, 0) + btrfs_item_size_nr(leaf, 0) !=
diff --git a/queue-4.4/btrfs-enhance-chunk-validation-check.patch b/queue-4.4/btrfs-enhance-chunk-validation-check.patch
new file mode 100644 (file)
index 0000000..7574f2b
--- /dev/null
@@ -0,0 +1,98 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo@cn.fujitsu.com>
+Date: Tue, 15 Dec 2015 09:14:37 +0800
+Subject: btrfs: Enhance chunk validation check
+
+From: Qu Wenruo <quwenruo@cn.fujitsu.com>
+
+commit f04b772bfc17f502703794f4d100d12155c1a1a9 upstream.
+
+Enhance chunk validation:
+1) Num_stripes
+   We already have such check but it's only in super block sys chunk
+   array.
+   Now check all on-disk chunks.
+
+2) Chunk logical
+   It should be aligned to sector size.
+   This behavior should be *DOUBLE CHECKED* for 64K sector size like
+   PPC64 or AArch64.
+   Maybe we can found some hidden bugs.
+
+3) Chunk length
+   Same as chunk logical, should be aligned to sector size.
+
+4) Stripe length
+   It should be power of 2.
+
+5) Chunk type
+   Any bit out of TYPE_MAS | PROFILE_MASK is invalid.
+
+With all these much restrict rules, several fuzzed image reported in
+mail list should no longer cause kernel panic.
+
+Reported-by: Vegard Nossum <vegard.nossum@oracle.com>
+Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
+Signed-off-by: Chris Mason <clm@fb.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/volumes.c |   33 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 32 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -6217,6 +6217,7 @@ static int read_one_chunk(struct btrfs_r
+       struct extent_map *em;
+       u64 logical;
+       u64 length;
++      u64 stripe_len;
+       u64 devid;
+       u8 uuid[BTRFS_UUID_SIZE];
+       int num_stripes;
+@@ -6225,6 +6226,37 @@ static int read_one_chunk(struct btrfs_r
+       logical = key->offset;
+       length = btrfs_chunk_length(leaf, chunk);
++      stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
++      num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
++      /* Validation check */
++      if (!num_stripes) {
++              btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
++                        num_stripes);
++              return -EIO;
++      }
++      if (!IS_ALIGNED(logical, root->sectorsize)) {
++              btrfs_err(root->fs_info,
++                        "invalid chunk logical %llu", logical);
++              return -EIO;
++      }
++      if (!length || !IS_ALIGNED(length, root->sectorsize)) {
++              btrfs_err(root->fs_info,
++                      "invalid chunk length %llu", length);
++              return -EIO;
++      }
++      if (!is_power_of_2(stripe_len)) {
++              btrfs_err(root->fs_info, "invalid chunk stripe length: %llu",
++                        stripe_len);
++              return -EIO;
++      }
++      if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
++          btrfs_chunk_type(leaf, chunk)) {
++              btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
++                        ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
++                          BTRFS_BLOCK_GROUP_PROFILE_MASK) &
++                        btrfs_chunk_type(leaf, chunk));
++              return -EIO;
++      }
+       read_lock(&map_tree->map_tree.lock);
+       em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
+@@ -6241,7 +6273,6 @@ static int read_one_chunk(struct btrfs_r
+       em = alloc_extent_map();
+       if (!em)
+               return -ENOMEM;
+-      num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+       map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS);
+       if (!map) {
+               free_extent_map(em);
diff --git a/queue-4.4/btrfs-fix-bug_on-in-btrfs_mark_buffer_dirty.patch b/queue-4.4/btrfs-fix-bug_on-in-btrfs_mark_buffer_dirty.patch
new file mode 100644 (file)
index 0000000..9a5ac20
--- /dev/null
@@ -0,0 +1,54 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Fri, 2 Sep 2016 12:35:34 -0700
+Subject: Btrfs: fix BUG_ON in btrfs_mark_buffer_dirty
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit ef85b25e982b5bba1530b936e283ef129f02ab9d upstream.
+
+This can only happen with CONFIG_BTRFS_FS_CHECK_INTEGRITY=y.
+
+Commit 1ba98d0 ("Btrfs: detect corruption when non-root leaf has zero item")
+assumes that a leaf is its root when leaf->bytenr == btrfs_root_bytenr(root),
+however, we should not use btrfs_root_bytenr(root) since it's mainly got
+updated during committing transaction.  So the check can fail when doing
+COW on this leaf while it is a root.
+
+This changes to use "if (leaf == btrfs_root_node(root))" instead, just like
+how we check whether leaf is a root in __btrfs_cow_block().
+
+Fixes: 1ba98d086fe3 (Btrfs: detect corruption when non-root leaf has zero item)
+Reported-by: Jeff Mahoney <jeffm@suse.com>
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -548,13 +548,17 @@ static noinline int check_leaf(struct bt
+                * open_ctree() some roots has not yet been set up.
+                */
+               if (!IS_ERR_OR_NULL(check_root)) {
++                      struct extent_buffer *eb;
++
++                      eb = btrfs_root_node(check_root);
+                       /* if leaf is the root, then it's fine */
+-                      if (leaf->start !=
+-                          btrfs_root_bytenr(&check_root->root_item)) {
++                      if (leaf != eb) {
+                               CORRUPT("non-root leaf's nritems is 0",
+-                                      leaf, root, 0);
++                                      leaf, check_root, 0);
++                              free_extent_buffer(eb);
+                               return -EIO;
+                       }
++                      free_extent_buffer(eb);
+               }
+               return 0;
+       }
diff --git a/queue-4.4/btrfs-fix-em-leak-in-find_first_block_group.patch b/queue-4.4/btrfs-fix-em-leak-in-find_first_block_group.patch
new file mode 100644 (file)
index 0000000..6de8d11
--- /dev/null
@@ -0,0 +1,31 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Josef Bacik <jbacik@fb.com>
+Date: Thu, 18 Aug 2016 15:30:06 -0400
+Subject: Btrfs: fix em leak in find_first_block_group
+
+From: Josef Bacik <jbacik@fb.com>
+
+commit 187ee58c62c1d0d238d3dc4835869d33e1869906 upstream.
+
+We need to call free_extent_map() on the em we look up.
+
+Signed-off-by: Josef Bacik <jbacik@fb.com>
+Reviewed-by: Omar Sandoval <osandov@fb.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Chris Mason <clm@fb.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent-tree.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -9518,6 +9518,7 @@ static int find_first_block_group(struct
+                       } else {
+                               ret = 0;
+                       }
++                      free_extent_map(em);
+                       goto out;
+               }
+               path->slots[0]++;
diff --git a/queue-4.4/btrfs-fix-emptiness-check-for-dirtied-extent-buffers-at-check_leaf.patch b/queue-4.4/btrfs-fix-emptiness-check-for-dirtied-extent-buffers-at-check_leaf.patch
new file mode 100644 (file)
index 0000000..d005fbf
--- /dev/null
@@ -0,0 +1,117 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Filipe Manana <fdmanana@suse.com>
+Date: Wed, 23 Nov 2016 16:21:18 +0000
+Subject: Btrfs: fix emptiness check for dirtied extent buffers at check_leaf()
+
+From: Filipe Manana <fdmanana@suse.com>
+
+commit f177d73949bf758542ca15a1c1945bd2e802cc65 upstream.
+
+We can not simply use the owner field from an extent buffer's header to
+get the id of the respective tree when the extent buffer is from a
+relocation tree. When we create the root for a relocation tree we leave
+(on purpose) the owner field with the same value as the subvolume's tree
+root (we do this at ctree.c:btrfs_copy_root()). So we must ignore extent
+buffers from relocation trees, which have the BTRFS_HEADER_FLAG_RELOC
+flag set, because otherwise we will always consider the extent buffer
+as not being the root of the tree (the root of original subvolume tree
+is always different from the root of the respective relocation tree).
+
+This lead to assertion failures when running with the integrity checker
+enabled (CONFIG_BTRFS_FS_CHECK_INTEGRITY=y) such as the following:
+
+[  643.393409] BTRFS critical (device sdg): corrupt leaf, non-root leaf's nritems is 0: block=38506496, root=260, slot=0
+[  643.397609] BTRFS info (device sdg): leaf 38506496 total ptrs 0 free space 3995
+[  643.407075] assertion failed: 0, file: fs/btrfs/disk-io.c, line: 4078
+[  643.408425] ------------[ cut here ]------------
+[  643.409112] kernel BUG at fs/btrfs/ctree.h:3419!
+[  643.409773] invalid opcode: 0000 [#1] PREEMPT SMP
+[  643.410447] Modules linked in: dm_flakey dm_mod crc32c_generic btrfs xor raid6_pq ppdev psmouse acpi_cpufreq parport_pc evdev parport tpm_tis tpm_tis_core pcspkr serio_raw i2c_piix4 sg tpm i2c_core button processor loop autofs4 ext4 crc16 jbd2 mbcache sr_mod cdrom sd_mod ata_generic virtio_scsi ata_piix libata virtio_pci virtio_ring scsi_mod virtio e1000 floppy
+[  643.414356] CPU: 11 PID: 32726 Comm: btrfs Not tainted 4.8.0-rc8-btrfs-next-35+ #1
+[  643.414356] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.9.1-0-gb3ef39f-prebuilt.qemu-project.org 04/01/2014
+[  643.414356] task: ffff880145e95b00 task.stack: ffff88014826c000
+[  643.414356] RIP: 0010:[<ffffffffa0352759>]  [<ffffffffa0352759>] assfail.constprop.41+0x1c/0x1e [btrfs]
+[  643.414356] RSP: 0018:ffff88014826fa28  EFLAGS: 00010292
+[  643.414356] RAX: 0000000000000039 RBX: ffff88014e2d7c38 RCX: 0000000000000001
+[  643.414356] RDX: ffff88023f4d2f58 RSI: ffffffff81806c63 RDI: 00000000ffffffff
+[  643.414356] RBP: ffff88014826fa28 R08: 0000000000000001 R09: 0000000000000000
+[  643.414356] R10: ffff88014826f918 R11: ffffffff82f3c5ed R12: ffff880172910000
+[  643.414356] R13: ffff880233992230 R14: ffff8801a68a3310 R15: fffffffffffffff8
+[  643.414356] FS:  00007f9ca305e8c0(0000) GS:ffff88023f4c0000(0000) knlGS:0000000000000000
+[  643.414356] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+[  643.414356] CR2: 00007f9ca3071000 CR3: 000000015d01b000 CR4: 00000000000006e0
+[  643.414356] Stack:
+[  643.414356]  ffff88014826fa50 ffffffffa02d655a 000000000000000a ffff88014e2d7c38
+[  643.414356]  0000000000000000 ffff88014826faa8 ffffffffa02b72f3 ffff88014826fab8
+[  643.414356]  00ffffffa03228e4 0000000000000000 0000000000000000 ffff8801bbd4e000
+[  643.414356] Call Trace:
+[  643.414356]  [<ffffffffa02d655a>] btrfs_mark_buffer_dirty+0xdf/0xe5 [btrfs]
+[  643.414356]  [<ffffffffa02b72f3>] btrfs_copy_root+0x18a/0x1d1 [btrfs]
+[  643.414356]  [<ffffffffa0322921>] create_reloc_root+0x72/0x1ba [btrfs]
+[  643.414356]  [<ffffffffa03267c2>] btrfs_init_reloc_root+0x7b/0xa7 [btrfs]
+[  643.414356]  [<ffffffffa02d9e44>] record_root_in_trans+0xdf/0xed [btrfs]
+[  643.414356]  [<ffffffffa02db04e>] btrfs_record_root_in_trans+0x50/0x6a [btrfs]
+[  643.414356]  [<ffffffffa030ad2b>] create_subvol+0x472/0x773 [btrfs]
+[  643.414356]  [<ffffffffa030b406>] btrfs_mksubvol+0x3da/0x463 [btrfs]
+[  643.414356]  [<ffffffffa030b406>] ? btrfs_mksubvol+0x3da/0x463 [btrfs]
+[  643.414356]  [<ffffffff810781ac>] ? preempt_count_add+0x65/0x68
+[  643.414356]  [<ffffffff811a6e97>] ? __mnt_want_write+0x62/0x77
+[  643.414356]  [<ffffffffa030b55d>] btrfs_ioctl_snap_create_transid+0xce/0x187 [btrfs]
+[  643.414356]  [<ffffffffa030b67d>] btrfs_ioctl_snap_create+0x67/0x81 [btrfs]
+[  643.414356]  [<ffffffffa030ecfd>] btrfs_ioctl+0x508/0x20dd [btrfs]
+[  643.414356]  [<ffffffff81293e39>] ? __this_cpu_preempt_check+0x13/0x15
+[  643.414356]  [<ffffffff81155eca>] ? handle_mm_fault+0x976/0x9ab
+[  643.414356]  [<ffffffff81091300>] ? arch_local_irq_save+0x9/0xc
+[  643.414356]  [<ffffffff8119a2b0>] vfs_ioctl+0x18/0x34
+[  643.414356]  [<ffffffff8119a8e8>] do_vfs_ioctl+0x581/0x600
+[  643.414356]  [<ffffffff814b9552>] ? entry_SYSCALL_64_fastpath+0x5/0xa8
+[  643.414356]  [<ffffffff81093fe9>] ? trace_hardirqs_on_caller+0x17b/0x197
+[  643.414356]  [<ffffffff8119a9be>] SyS_ioctl+0x57/0x79
+[  643.414356]  [<ffffffff814b9565>] entry_SYSCALL_64_fastpath+0x18/0xa8
+[  643.414356]  [<ffffffff81091b08>] ? trace_hardirqs_off_caller+0x3f/0xaa
+[  643.414356] Code: 89 83 88 00 00 00 31 c0 5b 41 5c 41 5d 5d c3 55 89 f1 48 c7 c2 98 bc 35 a0 48 89 fe 48 c7 c7 05 be 35 a0 48 89 e5 e8 13 46 dd e0 <0f> 0b 55 89 f1 48 c7 c2 9f d3 35 a0 48 89 fe 48 c7 c7 7a d5 35
+[  643.414356] RIP  [<ffffffffa0352759>] assfail.constprop.41+0x1c/0x1e [btrfs]
+[  643.414356]  RSP <ffff88014826fa28>
+[  643.468267] ---[ end trace 6a1b3fb1a9d7d6e3 ]---
+
+This can be easily reproduced by running xfstests with the integrity
+checker enabled.
+
+Fixes: 1ba98d086fe3 (Btrfs: detect corruption when non-root leaf has zero item)
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -536,7 +536,15 @@ static noinline int check_leaf(struct bt
+       u32 nritems = btrfs_header_nritems(leaf);
+       int slot;
+-      if (nritems == 0) {
++      /*
++       * Extent buffers from a relocation tree have a owner field that
++       * corresponds to the subvolume tree they are based on. So just from an
++       * extent buffer alone we can not find out what is the id of the
++       * corresponding subvolume tree, so we can not figure out if the extent
++       * buffer corresponds to the root of the relocation tree or not. So skip
++       * this check for relocation trees.
++       */
++      if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
+               struct btrfs_root *check_root;
+               key.objectid = btrfs_header_owner(leaf);
+@@ -564,6 +572,9 @@ static noinline int check_leaf(struct bt
+               return 0;
+       }
++      if (nritems == 0)
++              return 0;
++
+       /* Check the 0 item */
+       if (btrfs_item_offset_nr(leaf, 0) + btrfs_item_size_nr(leaf, 0) !=
+           BTRFS_LEAF_DATA_SIZE(root)) {
diff --git a/queue-4.4/btrfs-improve-check_node-to-avoid-reading-corrupted-nodes.patch b/queue-4.4/btrfs-improve-check_node-to-avoid-reading-corrupted-nodes.patch
new file mode 100644 (file)
index 0000000..29eb14d
--- /dev/null
@@ -0,0 +1,77 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Wed, 14 Sep 2016 17:23:24 -0700
+Subject: Btrfs: improve check_node to avoid reading corrupted nodes
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 6b722c1747d533ac6d4df110dc8233db46918b65 upstream.
+
+We need to check items in a node to make sure that we're reading
+a valid one, otherwise we could get various crashes while processing
+delayed_refs.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   32 ++++++++++++++++++++++++++++----
+ 1 file changed, 28 insertions(+), 4 deletions(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -523,9 +523,10 @@ static int check_tree_block_fsid(struct
+ }
+ #define CORRUPT(reason, eb, root, slot)                               \
+-      btrfs_crit(root->fs_info, "corrupt leaf, %s: block=%llu,"       \
+-                 "root=%llu, slot=%d", reason,                        \
+-             btrfs_header_bytenr(eb), root->objectid, slot)
++      btrfs_crit(root->fs_info, "corrupt %s, %s: block=%llu," \
++                 " root=%llu, slot=%d",                       \
++                 btrfs_header_level(eb) == 0 ? "leaf" : "node",\
++                 reason, btrfs_header_bytenr(eb), root->objectid, slot)
+ static noinline int check_leaf(struct btrfs_root *root,
+                              struct extent_buffer *leaf)
+@@ -616,6 +617,10 @@ static noinline int check_leaf(struct bt
+ static int check_node(struct btrfs_root *root, struct extent_buffer *node)
+ {
+       unsigned long nr = btrfs_header_nritems(node);
++      struct btrfs_key key, next_key;
++      int slot;
++      u64 bytenr;
++      int ret = 0;
+       if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
+               btrfs_crit(root->fs_info,
+@@ -623,7 +628,26 @@ static int check_node(struct btrfs_root
+                          node->start, root->objectid, nr);
+               return -EIO;
+       }
+-      return 0;
++
++      for (slot = 0; slot < nr - 1; slot++) {
++              bytenr = btrfs_node_blockptr(node, slot);
++              btrfs_node_key_to_cpu(node, &key, slot);
++              btrfs_node_key_to_cpu(node, &next_key, slot + 1);
++
++              if (!bytenr) {
++                      CORRUPT("invalid item slot", node, root, slot);
++                      ret = -EIO;
++                      goto out;
++              }
++
++              if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
++                      CORRUPT("bad key order", node, root, slot);
++                      ret = -EIO;
++                      goto out;
++              }
++      }
++out:
++      return ret;
+ }
+ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
diff --git a/queue-4.4/btrfs-kill-bug_on-in-run_delayed_tree_ref.patch b/queue-4.4/btrfs-kill-bug_on-in-run_delayed_tree_ref.patch
new file mode 100644 (file)
index 0000000..417ddd6
--- /dev/null
@@ -0,0 +1,39 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Wed, 14 Sep 2016 19:19:05 -0700
+Subject: Btrfs: kill BUG_ON in run_delayed_tree_ref
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 02794222c4132ac003e7281fb71f4ec1645ffc87 upstream.
+
+In a corrupted btrfs image, we can come across this BUG_ON and
+get an unreponsive system, but if we return errors instead,
+its caller can handle everything gracefully by aborting the current
+transaction.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent-tree.c |    8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -2342,7 +2342,13 @@ static int run_delayed_tree_ref(struct b
+               ins.type = BTRFS_EXTENT_ITEM_KEY;
+       }
+-      BUG_ON(node->ref_mod != 1);
++      if (node->ref_mod != 1) {
++              btrfs_err(root->fs_info,
++      "btree block(%llu) has %d references rather than 1: action %d ref_root %llu parent %llu",
++                        node->bytenr, node->ref_mod, node->action, ref_root,
++                        parent);
++              return -EIO;
++      }
+       if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) {
+               BUG_ON(!extent_op || !extent_op->update_flags);
+               ret = alloc_reserved_tree_block(trans, root,
diff --git a/queue-4.4/btrfs-memset-to-avoid-stale-content-in-btree-leaf.patch b/queue-4.4/btrfs-memset-to-avoid-stale-content-in-btree-leaf.patch
new file mode 100644 (file)
index 0000000..89d30bb
--- /dev/null
@@ -0,0 +1,114 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Fri, 23 Sep 2016 13:44:44 -0700
+Subject: Btrfs: memset to avoid stale content in btree leaf
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 851cd173f06045816528176001cf82948282029c upstream.
+
+This is an additional patch to
+"Btrfs: memset to avoid stale content in btree node block".
+
+This uses memset to initialize the unused space in a leaf to avoid
+potential stale content, which may be incurred by pushing items
+between sibling leaves.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/ctree.c     |   14 --------------
+ fs/btrfs/ctree.h     |   15 +++++++++++++++
+ fs/btrfs/extent_io.c |   18 +++++++++++++-----
+ 3 files changed, 28 insertions(+), 19 deletions(-)
+
+--- a/fs/btrfs/ctree.c
++++ b/fs/btrfs/ctree.c
+@@ -1726,20 +1726,6 @@ int btrfs_realloc_node(struct btrfs_tran
+       return err;
+ }
+-/*
+- * The leaf data grows from end-to-front in the node.
+- * this returns the address of the start of the last item,
+- * which is the stop of the leaf data stack
+- */
+-static inline unsigned int leaf_data_end(struct btrfs_root *root,
+-                                       struct extent_buffer *leaf)
+-{
+-      u32 nr = btrfs_header_nritems(leaf);
+-      if (nr == 0)
+-              return BTRFS_LEAF_DATA_SIZE(root);
+-      return btrfs_item_offset_nr(leaf, nr - 1);
+-}
+-
+ /*
+  * search for key in the extent_buffer.  The items start at offset p,
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -3158,6 +3158,21 @@ static inline unsigned long btrfs_leaf_d
+       return offsetof(struct btrfs_leaf, items);
+ }
++/*
++ * The leaf data grows from end-to-front in the node.
++ * this returns the address of the start of the last item,
++ * which is the stop of the leaf data stack
++ */
++static inline unsigned int leaf_data_end(struct btrfs_root *root,
++                                       struct extent_buffer *leaf)
++{
++      u32 nr = btrfs_header_nritems(leaf);
++
++      if (nr == 0)
++              return BTRFS_LEAF_DATA_SIZE(root);
++      return btrfs_item_offset_nr(leaf, nr - 1);
++}
++
+ /* struct btrfs_file_extent_item */
+ BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
+ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_bytenr,
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -3847,8 +3847,10 @@ static noinline_for_stack int write_one_
+       struct block_device *bdev = fs_info->fs_devices->latest_bdev;
+       struct extent_io_tree *tree = &BTRFS_I(fs_info->btree_inode)->io_tree;
+       u64 offset = eb->start;
++      u32 nritems;
+       unsigned long i, num_pages;
+       unsigned long bio_flags = 0;
++      unsigned long start, end;
+       int rw = (epd->sync_io ? WRITE_SYNC : WRITE) | REQ_META;
+       int ret = 0;
+@@ -3858,15 +3860,21 @@ static noinline_for_stack int write_one_
+       if (btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID)
+               bio_flags = EXTENT_BIO_TREE_LOG;
+-      /* set btree node beyond nritems with 0 to avoid stale content */
++      /* set btree blocks beyond nritems with 0 to avoid stale content. */
++      nritems = btrfs_header_nritems(eb);
+       if (btrfs_header_level(eb) > 0) {
+-              u32 nritems;
+-              unsigned long end;
+-
+-              nritems = btrfs_header_nritems(eb);
+               end = btrfs_node_key_ptr_offset(nritems);
+               memset_extent_buffer(eb, 0, end, eb->len - end);
++      } else {
++              /*
++               * leaf:
++               * header 0 1 2 .. N ... data_N .. data_2 data_1 data_0
++               */
++              start = btrfs_item_nr_offset(nritems);
++              end = btrfs_leaf_data(eb) +
++                    leaf_data_end(fs_info->tree_root, eb);
++              memset_extent_buffer(eb, 0, start, end - start);
+       }
+       for (i = 0; i < num_pages; i++) {
diff --git a/queue-4.4/btrfs-memset-to-avoid-stale-content-in-btree-node-block.patch b/queue-4.4/btrfs-memset-to-avoid-stale-content-in-btree-node-block.patch
new file mode 100644 (file)
index 0000000..357b3ae
--- /dev/null
@@ -0,0 +1,53 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Liu Bo <bo.li.liu@oracle.com>
+Date: Wed, 14 Sep 2016 17:22:57 -0700
+Subject: Btrfs: memset to avoid stale content in btree node block
+
+From: Liu Bo <bo.li.liu@oracle.com>
+
+commit 3eb548ee3a8042d95ad81be254e67a5222c24e03 upstream.
+
+During updating btree, we could push items between sibling
+nodes/leaves, for leaves data sections starts reversely from
+the end of the block while for nodes we only have key pairs
+which are stored one by one from the start of the block.
+
+So we could do try to push key pairs from one node to the next
+node right in the tree, and after that, we update the node's
+nritems to reflect the correct end while leaving the stale
+content in the node.  One may intentionally corrupt the fs
+image and access the stale content by bumping the nritems and
+causes various crashes.
+
+This takes the in-memory @nritems as the correct one and
+gets to memset the unused part of a btree node.
+
+Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent_io.c |   11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -3858,6 +3858,17 @@ static noinline_for_stack int write_one_
+       if (btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID)
+               bio_flags = EXTENT_BIO_TREE_LOG;
++      /* set btree node beyond nritems with 0 to avoid stale content */
++      if (btrfs_header_level(eb) > 0) {
++              u32 nritems;
++              unsigned long end;
++
++              nritems = btrfs_header_nritems(eb);
++              end = btrfs_node_key_ptr_offset(nritems);
++
++              memset_extent_buffer(eb, 0, end, eb->len - end);
++      }
++
+       for (i = 0; i < num_pages; i++) {
+               struct page *p = eb->pages[i];
diff --git a/queue-4.4/btrfs-move-leaf-and-node-validation-checker-to-tree-checker.c.patch b/queue-4.4/btrfs-move-leaf-and-node-validation-checker-to-tree-checker.c.patch
new file mode 100644 (file)
index 0000000..9679546
--- /dev/null
@@ -0,0 +1,699 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Date: Mon, 9 Oct 2017 01:51:02 +0000
+Subject: btrfs: Move leaf and node validation checker to tree-checker.c
+
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+
+commit 557ea5dd003d371536f6b4e8f7c8209a2b6fd4e3 upstream.
+
+It's no doubt the comprehensive tree block checker will become larger,
+so moving them into their own files is quite reasonable.
+
+Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
+[ wording adjustments ]
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4:
+ - The moved code is slightly different
+ - Adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/Makefile       |    2 
+ fs/btrfs/disk-io.c      |  284 --------------------------------------------
+ fs/btrfs/tree-checker.c |  309 ++++++++++++++++++++++++++++++++++++++++++++++++
+ fs/btrfs/tree-checker.h |   26 ++++
+ 4 files changed, 340 insertions(+), 281 deletions(-)
+ create mode 100644 fs/btrfs/tree-checker.c
+ create mode 100644 fs/btrfs/tree-checker.h
+
+--- a/fs/btrfs/Makefile
++++ b/fs/btrfs/Makefile
+@@ -9,7 +9,7 @@ btrfs-y += super.o ctree.o extent-tree.o
+          export.o tree-log.o free-space-cache.o zlib.o lzo.o \
+          compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
+          reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
+-         uuid-tree.o props.o hash.o
++         uuid-tree.o props.o hash.o tree-checker.o
+ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
+ btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -49,6 +49,7 @@
+ #include "raid56.h"
+ #include "sysfs.h"
+ #include "qgroup.h"
++#include "tree-checker.h"
+ #ifdef CONFIG_X86
+ #include <asm/cpufeature.h>
+@@ -522,283 +523,6 @@ static int check_tree_block_fsid(struct
+       return ret;
+ }
+-#define CORRUPT(reason, eb, root, slot)                               \
+-      btrfs_crit(root->fs_info, "corrupt %s, %s: block=%llu," \
+-                 " root=%llu, slot=%d",                       \
+-                 btrfs_header_level(eb) == 0 ? "leaf" : "node",\
+-                 reason, btrfs_header_bytenr(eb), root->objectid, slot)
+-
+-static int check_extent_data_item(struct btrfs_root *root,
+-                                struct extent_buffer *leaf,
+-                                struct btrfs_key *key, int slot)
+-{
+-      struct btrfs_file_extent_item *fi;
+-      u32 sectorsize = root->sectorsize;
+-      u32 item_size = btrfs_item_size_nr(leaf, slot);
+-
+-      if (!IS_ALIGNED(key->offset, sectorsize)) {
+-              CORRUPT("unaligned key offset for file extent",
+-                      leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-
+-      fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+-
+-      if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) {
+-              CORRUPT("invalid file extent type", leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-
+-      /*
+-       * Support for new compression/encrption must introduce incompat flag,
+-       * and must be caught in open_ctree().
+-       */
+-      if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) {
+-              CORRUPT("invalid file extent compression", leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-      if (btrfs_file_extent_encryption(leaf, fi)) {
+-              CORRUPT("invalid file extent encryption", leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-      if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
+-              /* Inline extent must have 0 as key offset */
+-              if (key->offset) {
+-                      CORRUPT("inline extent has non-zero key offset",
+-                              leaf, root, slot);
+-                      return -EUCLEAN;
+-              }
+-
+-              /* Compressed inline extent has no on-disk size, skip it */
+-              if (btrfs_file_extent_compression(leaf, fi) !=
+-                  BTRFS_COMPRESS_NONE)
+-                      return 0;
+-
+-              /* Uncompressed inline extent size must match item size */
+-              if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START +
+-                  btrfs_file_extent_ram_bytes(leaf, fi)) {
+-                      CORRUPT("plaintext inline extent has invalid size",
+-                              leaf, root, slot);
+-                      return -EUCLEAN;
+-              }
+-              return 0;
+-      }
+-
+-      /* Regular or preallocated extent has fixed item size */
+-      if (item_size != sizeof(*fi)) {
+-              CORRUPT(
+-              "regluar or preallocated extent data item size is invalid",
+-                      leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-      if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) ||
+-          !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) ||
+-          !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) ||
+-          !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) ||
+-          !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) {
+-              CORRUPT(
+-              "regular or preallocated extent data item has unaligned value",
+-                      leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-
+-      return 0;
+-}
+-
+-static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,
+-                         struct btrfs_key *key, int slot)
+-{
+-      u32 sectorsize = root->sectorsize;
+-      u32 csumsize = btrfs_super_csum_size(root->fs_info->super_copy);
+-
+-      if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) {
+-              CORRUPT("invalid objectid for csum item", leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-      if (!IS_ALIGNED(key->offset, sectorsize)) {
+-              CORRUPT("unaligned key offset for csum item", leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-      if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) {
+-              CORRUPT("unaligned csum item size", leaf, root, slot);
+-              return -EUCLEAN;
+-      }
+-      return 0;
+-}
+-
+-/*
+- * Common point to switch the item-specific validation.
+- */
+-static int check_leaf_item(struct btrfs_root *root,
+-                         struct extent_buffer *leaf,
+-                         struct btrfs_key *key, int slot)
+-{
+-      int ret = 0;
+-
+-      switch (key->type) {
+-      case BTRFS_EXTENT_DATA_KEY:
+-              ret = check_extent_data_item(root, leaf, key, slot);
+-              break;
+-      case BTRFS_EXTENT_CSUM_KEY:
+-              ret = check_csum_item(root, leaf, key, slot);
+-              break;
+-      }
+-      return ret;
+-}
+-
+-static noinline int check_leaf(struct btrfs_root *root,
+-                             struct extent_buffer *leaf)
+-{
+-      /* No valid key type is 0, so all key should be larger than this key */
+-      struct btrfs_key prev_key = {0, 0, 0};
+-      struct btrfs_key key;
+-      u32 nritems = btrfs_header_nritems(leaf);
+-      int slot;
+-
+-      /*
+-       * Extent buffers from a relocation tree have a owner field that
+-       * corresponds to the subvolume tree they are based on. So just from an
+-       * extent buffer alone we can not find out what is the id of the
+-       * corresponding subvolume tree, so we can not figure out if the extent
+-       * buffer corresponds to the root of the relocation tree or not. So skip
+-       * this check for relocation trees.
+-       */
+-      if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
+-              struct btrfs_root *check_root;
+-
+-              key.objectid = btrfs_header_owner(leaf);
+-              key.type = BTRFS_ROOT_ITEM_KEY;
+-              key.offset = (u64)-1;
+-
+-              check_root = btrfs_get_fs_root(root->fs_info, &key, false);
+-              /*
+-               * The only reason we also check NULL here is that during
+-               * open_ctree() some roots has not yet been set up.
+-               */
+-              if (!IS_ERR_OR_NULL(check_root)) {
+-                      struct extent_buffer *eb;
+-
+-                      eb = btrfs_root_node(check_root);
+-                      /* if leaf is the root, then it's fine */
+-                      if (leaf != eb) {
+-                              CORRUPT("non-root leaf's nritems is 0",
+-                                      leaf, check_root, 0);
+-                              free_extent_buffer(eb);
+-                              return -EUCLEAN;
+-                      }
+-                      free_extent_buffer(eb);
+-              }
+-              return 0;
+-      }
+-
+-      if (nritems == 0)
+-              return 0;
+-
+-      /*
+-       * Check the following things to make sure this is a good leaf, and
+-       * leaf users won't need to bother with similar sanity checks:
+-       *
+-       * 1) key order
+-       * 2) item offset and size
+-       *    No overlap, no hole, all inside the leaf.
+-       * 3) item content
+-       *    If possible, do comprehensive sanity check.
+-       *    NOTE: All checks must only rely on the item data itself.
+-       */
+-      for (slot = 0; slot < nritems; slot++) {
+-              u32 item_end_expected;
+-              int ret;
+-
+-              btrfs_item_key_to_cpu(leaf, &key, slot);
+-
+-              /* Make sure the keys are in the right order */
+-              if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
+-                      CORRUPT("bad key order", leaf, root, slot);
+-                      return -EUCLEAN;
+-              }
+-
+-              /*
+-               * Make sure the offset and ends are right, remember that the
+-               * item data starts at the end of the leaf and grows towards the
+-               * front.
+-               */
+-              if (slot == 0)
+-                      item_end_expected = BTRFS_LEAF_DATA_SIZE(root);
+-              else
+-                      item_end_expected = btrfs_item_offset_nr(leaf,
+-                                                               slot - 1);
+-              if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
+-                      CORRUPT("slot offset bad", leaf, root, slot);
+-                      return -EUCLEAN;
+-              }
+-
+-              /*
+-               * Check to make sure that we don't point outside of the leaf,
+-               * just incase all the items are consistent to eachother, but
+-               * all point outside of the leaf.
+-               */
+-              if (btrfs_item_end_nr(leaf, slot) >
+-                  BTRFS_LEAF_DATA_SIZE(root)) {
+-                      CORRUPT("slot end outside of leaf", leaf, root, slot);
+-                      return -EUCLEAN;
+-              }
+-
+-              /* Also check if the item pointer overlaps with btrfs item. */
+-              if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) >
+-                  btrfs_item_ptr_offset(leaf, slot)) {
+-                      CORRUPT("slot overlap with its data", leaf, root, slot);
+-                      return -EUCLEAN;
+-              }
+-
+-              /* Check if the item size and content meet other criteria */
+-              ret = check_leaf_item(root, leaf, &key, slot);
+-              if (ret < 0)
+-                      return ret;
+-
+-              prev_key.objectid = key.objectid;
+-              prev_key.type = key.type;
+-              prev_key.offset = key.offset;
+-      }
+-
+-      return 0;
+-}
+-
+-static int check_node(struct btrfs_root *root, struct extent_buffer *node)
+-{
+-      unsigned long nr = btrfs_header_nritems(node);
+-      struct btrfs_key key, next_key;
+-      int slot;
+-      u64 bytenr;
+-      int ret = 0;
+-
+-      if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
+-              btrfs_crit(root->fs_info,
+-                         "corrupt node: block %llu root %llu nritems %lu",
+-                         node->start, root->objectid, nr);
+-              return -EIO;
+-      }
+-
+-      for (slot = 0; slot < nr - 1; slot++) {
+-              bytenr = btrfs_node_blockptr(node, slot);
+-              btrfs_node_key_to_cpu(node, &key, slot);
+-              btrfs_node_key_to_cpu(node, &next_key, slot + 1);
+-
+-              if (!bytenr) {
+-                      CORRUPT("invalid item slot", node, root, slot);
+-                      ret = -EIO;
+-                      goto out;
+-              }
+-
+-              if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
+-                      CORRUPT("bad key order", node, root, slot);
+-                      ret = -EIO;
+-                      goto out;
+-              }
+-      }
+-out:
+-      return ret;
+-}
+-
+ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
+                                     u64 phy_offset, struct page *page,
+                                     u64 start, u64 end, int mirror)
+@@ -865,12 +589,12 @@ static int btree_readpage_end_io_hook(st
+        * that we don't try and read the other copies of this block, just
+        * return -EIO.
+        */
+-      if (found_level == 0 && check_leaf(root, eb)) {
++      if (found_level == 0 && btrfs_check_leaf(root, eb)) {
+               set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
+               ret = -EIO;
+       }
+-      if (found_level > 0 && check_node(root, eb))
++      if (found_level > 0 && btrfs_check_node(root, eb))
+               ret = -EIO;
+       if (!ret)
+@@ -4172,7 +3896,7 @@ void btrfs_mark_buffer_dirty(struct exte
+                                    buf->len,
+                                    root->fs_info->dirty_metadata_batch);
+ #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+-      if (btrfs_header_level(buf) == 0 && check_leaf(root, buf)) {
++      if (btrfs_header_level(buf) == 0 && btrfs_check_leaf(root, buf)) {
+               btrfs_print_leaf(root, buf);
+               ASSERT(0);
+       }
+--- /dev/null
++++ b/fs/btrfs/tree-checker.c
+@@ -0,0 +1,309 @@
++/*
++ * Copyright (C) Qu Wenruo 2017.  All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public
++ * License v2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public
++ * License along with this program.
++ */
++
++/*
++ * The module is used to catch unexpected/corrupted tree block data.
++ * Such behavior can be caused either by a fuzzed image or bugs.
++ *
++ * The objective is to do leaf/node validation checks when tree block is read
++ * from disk, and check *every* possible member, so other code won't
++ * need to checking them again.
++ *
++ * Due to the potential and unwanted damage, every checker needs to be
++ * carefully reviewed otherwise so it does not prevent mount of valid images.
++ */
++
++#include "ctree.h"
++#include "tree-checker.h"
++#include "disk-io.h"
++#include "compression.h"
++
++#define CORRUPT(reason, eb, root, slot)                                       \
++      btrfs_crit(root->fs_info,                                       \
++                 "corrupt %s, %s: block=%llu, root=%llu, slot=%d",    \
++                 btrfs_header_level(eb) == 0 ? "leaf" : "node",       \
++                 reason, btrfs_header_bytenr(eb), root->objectid, slot)
++
++static int check_extent_data_item(struct btrfs_root *root,
++                                struct extent_buffer *leaf,
++                                struct btrfs_key *key, int slot)
++{
++      struct btrfs_file_extent_item *fi;
++      u32 sectorsize = root->sectorsize;
++      u32 item_size = btrfs_item_size_nr(leaf, slot);
++
++      if (!IS_ALIGNED(key->offset, sectorsize)) {
++              CORRUPT("unaligned key offset for file extent",
++                      leaf, root, slot);
++              return -EUCLEAN;
++      }
++
++      fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
++
++      if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) {
++              CORRUPT("invalid file extent type", leaf, root, slot);
++              return -EUCLEAN;
++      }
++
++      /*
++       * Support for new compression/encrption must introduce incompat flag,
++       * and must be caught in open_ctree().
++       */
++      if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) {
++              CORRUPT("invalid file extent compression", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (btrfs_file_extent_encryption(leaf, fi)) {
++              CORRUPT("invalid file extent encryption", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
++              /* Inline extent must have 0 as key offset */
++              if (key->offset) {
++                      CORRUPT("inline extent has non-zero key offset",
++                              leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
++              /* Compressed inline extent has no on-disk size, skip it */
++              if (btrfs_file_extent_compression(leaf, fi) !=
++                  BTRFS_COMPRESS_NONE)
++                      return 0;
++
++              /* Uncompressed inline extent size must match item size */
++              if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START +
++                  btrfs_file_extent_ram_bytes(leaf, fi)) {
++                      CORRUPT("plaintext inline extent has invalid size",
++                              leaf, root, slot);
++                      return -EUCLEAN;
++              }
++              return 0;
++      }
++
++      /* Regular or preallocated extent has fixed item size */
++      if (item_size != sizeof(*fi)) {
++              CORRUPT(
++              "regluar or preallocated extent data item size is invalid",
++                      leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) ||
++          !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) {
++              CORRUPT(
++              "regular or preallocated extent data item has unaligned value",
++                      leaf, root, slot);
++              return -EUCLEAN;
++      }
++
++      return 0;
++}
++
++static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,
++                         struct btrfs_key *key, int slot)
++{
++      u32 sectorsize = root->sectorsize;
++      u32 csumsize = btrfs_super_csum_size(root->fs_info->super_copy);
++
++      if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) {
++              CORRUPT("invalid objectid for csum item", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (!IS_ALIGNED(key->offset, sectorsize)) {
++              CORRUPT("unaligned key offset for csum item", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) {
++              CORRUPT("unaligned csum item size", leaf, root, slot);
++              return -EUCLEAN;
++      }
++      return 0;
++}
++
++/*
++ * Common point to switch the item-specific validation.
++ */
++static int check_leaf_item(struct btrfs_root *root,
++                         struct extent_buffer *leaf,
++                         struct btrfs_key *key, int slot)
++{
++      int ret = 0;
++
++      switch (key->type) {
++      case BTRFS_EXTENT_DATA_KEY:
++              ret = check_extent_data_item(root, leaf, key, slot);
++              break;
++      case BTRFS_EXTENT_CSUM_KEY:
++              ret = check_csum_item(root, leaf, key, slot);
++              break;
++      }
++      return ret;
++}
++
++int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf)
++{
++      struct btrfs_fs_info *fs_info = root->fs_info;
++      /* No valid key type is 0, so all key should be larger than this key */
++      struct btrfs_key prev_key = {0, 0, 0};
++      struct btrfs_key key;
++      u32 nritems = btrfs_header_nritems(leaf);
++      int slot;
++
++      /*
++       * Extent buffers from a relocation tree have a owner field that
++       * corresponds to the subvolume tree they are based on. So just from an
++       * extent buffer alone we can not find out what is the id of the
++       * corresponding subvolume tree, so we can not figure out if the extent
++       * buffer corresponds to the root of the relocation tree or not. So
++       * skip this check for relocation trees.
++       */
++      if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
++              struct btrfs_root *check_root;
++
++              key.objectid = btrfs_header_owner(leaf);
++              key.type = BTRFS_ROOT_ITEM_KEY;
++              key.offset = (u64)-1;
++
++              check_root = btrfs_get_fs_root(fs_info, &key, false);
++              /*
++               * The only reason we also check NULL here is that during
++               * open_ctree() some roots has not yet been set up.
++               */
++              if (!IS_ERR_OR_NULL(check_root)) {
++                      struct extent_buffer *eb;
++
++                      eb = btrfs_root_node(check_root);
++                      /* if leaf is the root, then it's fine */
++                      if (leaf != eb) {
++                              CORRUPT("non-root leaf's nritems is 0",
++                                      leaf, check_root, 0);
++                              free_extent_buffer(eb);
++                              return -EUCLEAN;
++                      }
++                      free_extent_buffer(eb);
++              }
++              return 0;
++      }
++
++      if (nritems == 0)
++              return 0;
++
++      /*
++       * Check the following things to make sure this is a good leaf, and
++       * leaf users won't need to bother with similar sanity checks:
++       *
++       * 1) key ordering
++       * 2) item offset and size
++       *    No overlap, no hole, all inside the leaf.
++       * 3) item content
++       *    If possible, do comprehensive sanity check.
++       *    NOTE: All checks must only rely on the item data itself.
++       */
++      for (slot = 0; slot < nritems; slot++) {
++              u32 item_end_expected;
++              int ret;
++
++              btrfs_item_key_to_cpu(leaf, &key, slot);
++
++              /* Make sure the keys are in the right order */
++              if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
++                      CORRUPT("bad key order", leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
++              /*
++               * Make sure the offset and ends are right, remember that the
++               * item data starts at the end of the leaf and grows towards the
++               * front.
++               */
++              if (slot == 0)
++                      item_end_expected = BTRFS_LEAF_DATA_SIZE(root);
++              else
++                      item_end_expected = btrfs_item_offset_nr(leaf,
++                                                               slot - 1);
++              if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
++                      CORRUPT("slot offset bad", leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
++              /*
++               * Check to make sure that we don't point outside of the leaf,
++               * just in case all the items are consistent to each other, but
++               * all point outside of the leaf.
++               */
++              if (btrfs_item_end_nr(leaf, slot) >
++                  BTRFS_LEAF_DATA_SIZE(root)) {
++                      CORRUPT("slot end outside of leaf", leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
++              /* Also check if the item pointer overlaps with btrfs item. */
++              if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) >
++                  btrfs_item_ptr_offset(leaf, slot)) {
++                      CORRUPT("slot overlap with its data", leaf, root, slot);
++                      return -EUCLEAN;
++              }
++
++              /* Check if the item size and content meet other criteria */
++              ret = check_leaf_item(root, leaf, &key, slot);
++              if (ret < 0)
++                      return ret;
++
++              prev_key.objectid = key.objectid;
++              prev_key.type = key.type;
++              prev_key.offset = key.offset;
++      }
++
++      return 0;
++}
++
++int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node)
++{
++      unsigned long nr = btrfs_header_nritems(node);
++      struct btrfs_key key, next_key;
++      int slot;
++      u64 bytenr;
++      int ret = 0;
++
++      if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
++              btrfs_crit(root->fs_info,
++                         "corrupt node: block %llu root %llu nritems %lu",
++                         node->start, root->objectid, nr);
++              return -EIO;
++      }
++
++      for (slot = 0; slot < nr - 1; slot++) {
++              bytenr = btrfs_node_blockptr(node, slot);
++              btrfs_node_key_to_cpu(node, &key, slot);
++              btrfs_node_key_to_cpu(node, &next_key, slot + 1);
++
++              if (!bytenr) {
++                      CORRUPT("invalid item slot", node, root, slot);
++                      ret = -EIO;
++                      goto out;
++              }
++
++              if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
++                      CORRUPT("bad key order", node, root, slot);
++                      ret = -EIO;
++                      goto out;
++              }
++      }
++out:
++      return ret;
++}
+--- /dev/null
++++ b/fs/btrfs/tree-checker.h
+@@ -0,0 +1,26 @@
++/*
++ * Copyright (C) Qu Wenruo 2017.  All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public
++ * License v2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public
++ * License along with this program.
++ */
++
++#ifndef __BTRFS_TREE_CHECKER__
++#define __BTRFS_TREE_CHECKER__
++
++#include "ctree.h"
++#include "extent_io.h"
++
++int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf);
++int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node);
++
++#endif
diff --git a/queue-4.4/btrfs-refactor-check_leaf-function-for-later-expansion.patch b/queue-4.4/btrfs-refactor-check_leaf-function-for-later-expansion.patch
new file mode 100644 (file)
index 0000000..68547cb
--- /dev/null
@@ -0,0 +1,137 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Date: Wed, 23 Aug 2017 16:57:56 +0900
+Subject: btrfs: Refactor check_leaf function for later expansion
+
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+
+commit c3267bbaa9cae09b62960eafe33ad19196803285 upstream.
+
+Current check_leaf() function does a good job checking key order and
+item offset/size.
+
+However it only checks from slot 0 to the last but one slot, this is
+good but makes later expansion hard.
+
+So this refactoring iterates from slot 0 to the last slot.
+For key comparison, it uses a key with all 0 as initial key, so all
+valid keys should be larger than that.
+
+And for item size/offset checks, it compares current item end with
+previous item offset.
+For slot 0, use leaf end as a special case.
+
+This makes later item/key offset checks and item size checks easier to
+be implemented.
+
+Also, makes check_leaf() to return -EUCLEAN other than -EIO to indicate
+error.
+
+Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4:
+ - BTRFS_LEAF_DATA_SIZE() takes a root rather than an fs_info
+ - Adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c |   50 +++++++++++++++++++++++++++-----------------------
+ 1 file changed, 27 insertions(+), 23 deletions(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -531,8 +531,9 @@ static int check_tree_block_fsid(struct
+ static noinline int check_leaf(struct btrfs_root *root,
+                              struct extent_buffer *leaf)
+ {
++      /* No valid key type is 0, so all key should be larger than this key */
++      struct btrfs_key prev_key = {0, 0, 0};
+       struct btrfs_key key;
+-      struct btrfs_key leaf_key;
+       u32 nritems = btrfs_header_nritems(leaf);
+       int slot;
+@@ -565,7 +566,7 @@ static noinline int check_leaf(struct bt
+                               CORRUPT("non-root leaf's nritems is 0",
+                                       leaf, check_root, 0);
+                               free_extent_buffer(eb);
+-                              return -EIO;
++                              return -EUCLEAN;
+                       }
+                       free_extent_buffer(eb);
+               }
+@@ -575,28 +576,23 @@ static noinline int check_leaf(struct bt
+       if (nritems == 0)
+               return 0;
+-      /* Check the 0 item */
+-      if (btrfs_item_offset_nr(leaf, 0) + btrfs_item_size_nr(leaf, 0) !=
+-          BTRFS_LEAF_DATA_SIZE(root)) {
+-              CORRUPT("invalid item offset size pair", leaf, root, 0);
+-              return -EIO;
+-      }
+-
+       /*
+-       * Check to make sure each items keys are in the correct order and their
+-       * offsets make sense.  We only have to loop through nritems-1 because
+-       * we check the current slot against the next slot, which verifies the
+-       * next slot's offset+size makes sense and that the current's slot
+-       * offset is correct.
++       * Check the following things to make sure this is a good leaf, and
++       * leaf users won't need to bother with similar sanity checks:
++       *
++       * 1) key order
++       * 2) item offset and size
++       *    No overlap, no hole, all inside the leaf.
+        */
+-      for (slot = 0; slot < nritems - 1; slot++) {
+-              btrfs_item_key_to_cpu(leaf, &leaf_key, slot);
+-              btrfs_item_key_to_cpu(leaf, &key, slot + 1);
++      for (slot = 0; slot < nritems; slot++) {
++              u32 item_end_expected;
++
++              btrfs_item_key_to_cpu(leaf, &key, slot);
+               /* Make sure the keys are in the right order */
+-              if (btrfs_comp_cpu_keys(&leaf_key, &key) >= 0) {
++              if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
+                       CORRUPT("bad key order", leaf, root, slot);
+-                      return -EIO;
++                      return -EUCLEAN;
+               }
+               /*
+@@ -604,10 +600,14 @@ static noinline int check_leaf(struct bt
+                * item data starts at the end of the leaf and grows towards the
+                * front.
+                */
+-              if (btrfs_item_offset_nr(leaf, slot) !=
+-                      btrfs_item_end_nr(leaf, slot + 1)) {
++              if (slot == 0)
++                      item_end_expected = BTRFS_LEAF_DATA_SIZE(root);
++              else
++                      item_end_expected = btrfs_item_offset_nr(leaf,
++                                                               slot - 1);
++              if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
+                       CORRUPT("slot offset bad", leaf, root, slot);
+-                      return -EIO;
++                      return -EUCLEAN;
+               }
+               /*
+@@ -618,8 +618,12 @@ static noinline int check_leaf(struct bt
+               if (btrfs_item_end_nr(leaf, slot) >
+                   BTRFS_LEAF_DATA_SIZE(root)) {
+                       CORRUPT("slot end outside of leaf", leaf, root, slot);
+-                      return -EIO;
++                      return -EUCLEAN;
+               }
++
++              prev_key.objectid = key.objectid;
++              prev_key.type = key.type;
++              prev_key.offset = key.offset;
+       }
+       return 0;
diff --git a/queue-4.4/btrfs-struct-funcs-constify-readers.patch b/queue-4.4/btrfs-struct-funcs-constify-readers.patch
new file mode 100644 (file)
index 0000000..aaec941
--- /dev/null
@@ -0,0 +1,532 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Jeff Mahoney <jeffm@suse.com>
+Date: Wed, 28 Jun 2017 21:56:53 -0600
+Subject: btrfs: struct-funcs, constify readers
+
+From: Jeff Mahoney <jeffm@suse.com>
+
+commit 1cbb1f454e5321e47fc1e6b233066c7ccc979d15 upstream.
+
+We have reader helpers for most of the on-disk structures that use
+an extent_buffer and pointer as offset into the buffer that are
+read-only.  We should mark them as const and, in turn, allow consumers
+of these interfaces to mark the buffers const as well.
+
+No impact on code, but serves as documentation that a buffer is intended
+not to be modified.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/ctree.h        |  128 ++++++++++++++++++++++++------------------------
+ fs/btrfs/extent_io.c    |   24 ++++-----
+ fs/btrfs/extent_io.h    |   19 +++----
+ fs/btrfs/struct-funcs.c |    9 +--
+ 4 files changed, 91 insertions(+), 89 deletions(-)
+
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -2283,7 +2283,7 @@ do {
+ #define BTRFS_INODE_ROOT_ITEM_INIT    (1 << 31)
+ struct btrfs_map_token {
+-      struct extent_buffer *eb;
++      const struct extent_buffer *eb;
+       char *kaddr;
+       unsigned long offset;
+ };
+@@ -2314,18 +2314,19 @@ static inline void btrfs_init_map_token
+                          sizeof(((type *)0)->member)))
+ #define DECLARE_BTRFS_SETGET_BITS(bits)                                       \
+-u##bits btrfs_get_token_##bits(struct extent_buffer *eb, void *ptr,   \
+-                             unsigned long off,                       \
+-                              struct btrfs_map_token *token);         \
+-void btrfs_set_token_##bits(struct extent_buffer *eb, void *ptr,      \
++u##bits btrfs_get_token_##bits(const struct extent_buffer *eb,                \
++                             const void *ptr, unsigned long off,      \
++                             struct btrfs_map_token *token);          \
++void btrfs_set_token_##bits(struct extent_buffer *eb, const void *ptr,        \
+                           unsigned long off, u##bits val,             \
+                           struct btrfs_map_token *token);             \
+-static inline u##bits btrfs_get_##bits(struct extent_buffer *eb, void *ptr, \
++static inline u##bits btrfs_get_##bits(const struct extent_buffer *eb,        \
++                                     const void *ptr,                 \
+                                      unsigned long off)               \
+ {                                                                     \
+       return btrfs_get_token_##bits(eb, ptr, off, NULL);              \
+ }                                                                     \
+-static inline void btrfs_set_##bits(struct extent_buffer *eb, void *ptr, \
++static inline void btrfs_set_##bits(struct extent_buffer *eb, void *ptr,\
+                                   unsigned long off, u##bits val)     \
+ {                                                                     \
+        btrfs_set_token_##bits(eb, ptr, off, val, NULL);                       \
+@@ -2337,7 +2338,8 @@ DECLARE_BTRFS_SETGET_BITS(32)
+ DECLARE_BTRFS_SETGET_BITS(64)
+ #define BTRFS_SETGET_FUNCS(name, type, member, bits)                  \
+-static inline u##bits btrfs_##name(struct extent_buffer *eb, type *s) \
++static inline u##bits btrfs_##name(const struct extent_buffer *eb,    \
++                                 const type *s)                       \
+ {                                                                     \
+       BUILD_BUG_ON(sizeof(u##bits) != sizeof(((type *)0))->member);   \
+       return btrfs_get_##bits(eb, s, offsetof(type, member));         \
+@@ -2348,7 +2350,8 @@ static inline void btrfs_set_##name(stru
+       BUILD_BUG_ON(sizeof(u##bits) != sizeof(((type *)0))->member);   \
+       btrfs_set_##bits(eb, s, offsetof(type, member), val);           \
+ }                                                                     \
+-static inline u##bits btrfs_token_##name(struct extent_buffer *eb, type *s, \
++static inline u##bits btrfs_token_##name(const struct extent_buffer *eb,\
++                                       const type *s,                 \
+                                        struct btrfs_map_token *token) \
+ {                                                                     \
+       BUILD_BUG_ON(sizeof(u##bits) != sizeof(((type *)0))->member);   \
+@@ -2363,9 +2366,9 @@ static inline void btrfs_set_token_##nam
+ }
+ #define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits)           \
+-static inline u##bits btrfs_##name(struct extent_buffer *eb)          \
++static inline u##bits btrfs_##name(const struct extent_buffer *eb)    \
+ {                                                                     \
+-      type *p = page_address(eb->pages[0]);                           \
++      const type *p = page_address(eb->pages[0]);                     \
+       u##bits res = le##bits##_to_cpu(p->member);                     \
+       return res;                                                     \
+ }                                                                     \
+@@ -2377,7 +2380,7 @@ static inline void btrfs_set_##name(stru
+ }
+ #define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits)            \
+-static inline u##bits btrfs_##name(type *s)                           \
++static inline u##bits btrfs_##name(const type *s)                     \
+ {                                                                     \
+       return le##bits##_to_cpu(s->member);                            \
+ }                                                                     \
+@@ -2678,7 +2681,7 @@ static inline unsigned long btrfs_node_k
+               sizeof(struct btrfs_key_ptr) * nr;
+ }
+-void btrfs_node_key(struct extent_buffer *eb,
++void btrfs_node_key(const struct extent_buffer *eb,
+                   struct btrfs_disk_key *disk_key, int nr);
+ static inline void btrfs_set_node_key(struct extent_buffer *eb,
+@@ -2707,28 +2710,28 @@ static inline struct btrfs_item *btrfs_i
+       return (struct btrfs_item *)btrfs_item_nr_offset(nr);
+ }
+-static inline u32 btrfs_item_end(struct extent_buffer *eb,
++static inline u32 btrfs_item_end(const struct extent_buffer *eb,
+                                struct btrfs_item *item)
+ {
+       return btrfs_item_offset(eb, item) + btrfs_item_size(eb, item);
+ }
+-static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr)
++static inline u32 btrfs_item_end_nr(const struct extent_buffer *eb, int nr)
+ {
+       return btrfs_item_end(eb, btrfs_item_nr(nr));
+ }
+-static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr)
++static inline u32 btrfs_item_offset_nr(const struct extent_buffer *eb, int nr)
+ {
+       return btrfs_item_offset(eb, btrfs_item_nr(nr));
+ }
+-static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr)
++static inline u32 btrfs_item_size_nr(const struct extent_buffer *eb, int nr)
+ {
+       return btrfs_item_size(eb, btrfs_item_nr(nr));
+ }
+-static inline void btrfs_item_key(struct extent_buffer *eb,
++static inline void btrfs_item_key(const struct extent_buffer *eb,
+                          struct btrfs_disk_key *disk_key, int nr)
+ {
+       struct btrfs_item *item = btrfs_item_nr(nr);
+@@ -2764,8 +2767,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_dir_name_
+ BTRFS_SETGET_STACK_FUNCS(stack_dir_transid, struct btrfs_dir_item,
+                        transid, 64);
+-static inline void btrfs_dir_item_key(struct extent_buffer *eb,
+-                                    struct btrfs_dir_item *item,
++static inline void btrfs_dir_item_key(const struct extent_buffer *eb,
++                                    const struct btrfs_dir_item *item,
+                                     struct btrfs_disk_key *key)
+ {
+       read_eb_member(eb, item, struct btrfs_dir_item, location, key);
+@@ -2773,7 +2776,7 @@ static inline void btrfs_dir_item_key(st
+ static inline void btrfs_set_dir_item_key(struct extent_buffer *eb,
+                                         struct btrfs_dir_item *item,
+-                                        struct btrfs_disk_key *key)
++                                        const struct btrfs_disk_key *key)
+ {
+       write_eb_member(eb, item, struct btrfs_dir_item, location, key);
+ }
+@@ -2785,8 +2788,8 @@ BTRFS_SETGET_FUNCS(free_space_bitmaps, s
+ BTRFS_SETGET_FUNCS(free_space_generation, struct btrfs_free_space_header,
+                  generation, 64);
+-static inline void btrfs_free_space_key(struct extent_buffer *eb,
+-                                      struct btrfs_free_space_header *h,
++static inline void btrfs_free_space_key(const struct extent_buffer *eb,
++                                      const struct btrfs_free_space_header *h,
+                                       struct btrfs_disk_key *key)
+ {
+       read_eb_member(eb, h, struct btrfs_free_space_header, location, key);
+@@ -2794,7 +2797,7 @@ static inline void btrfs_free_space_key(
+ static inline void btrfs_set_free_space_key(struct extent_buffer *eb,
+                                           struct btrfs_free_space_header *h,
+-                                          struct btrfs_disk_key *key)
++                                          const struct btrfs_disk_key *key)
+ {
+       write_eb_member(eb, h, struct btrfs_free_space_header, location, key);
+ }
+@@ -2821,25 +2824,25 @@ static inline void btrfs_cpu_key_to_disk
+       disk->objectid = cpu_to_le64(cpu->objectid);
+ }
+-static inline void btrfs_node_key_to_cpu(struct extent_buffer *eb,
+-                                struct btrfs_key *key, int nr)
++static inline void btrfs_node_key_to_cpu(const struct extent_buffer *eb,
++                                       struct btrfs_key *key, int nr)
+ {
+       struct btrfs_disk_key disk_key;
+       btrfs_node_key(eb, &disk_key, nr);
+       btrfs_disk_key_to_cpu(key, &disk_key);
+ }
+-static inline void btrfs_item_key_to_cpu(struct extent_buffer *eb,
+-                                struct btrfs_key *key, int nr)
++static inline void btrfs_item_key_to_cpu(const struct extent_buffer *eb,
++                                       struct btrfs_key *key, int nr)
+ {
+       struct btrfs_disk_key disk_key;
+       btrfs_item_key(eb, &disk_key, nr);
+       btrfs_disk_key_to_cpu(key, &disk_key);
+ }
+-static inline void btrfs_dir_item_key_to_cpu(struct extent_buffer *eb,
+-                                    struct btrfs_dir_item *item,
+-                                    struct btrfs_key *key)
++static inline void btrfs_dir_item_key_to_cpu(const struct extent_buffer *eb,
++                                           const struct btrfs_dir_item *item,
++                                           struct btrfs_key *key)
+ {
+       struct btrfs_disk_key disk_key;
+       btrfs_dir_item_key(eb, item, &disk_key);
+@@ -2872,7 +2875,7 @@ BTRFS_SETGET_STACK_FUNCS(stack_header_nr
+                        nritems, 32);
+ BTRFS_SETGET_STACK_FUNCS(stack_header_bytenr, struct btrfs_header, bytenr, 64);
+-static inline int btrfs_header_flag(struct extent_buffer *eb, u64 flag)
++static inline int btrfs_header_flag(const struct extent_buffer *eb, u64 flag)
+ {
+       return (btrfs_header_flags(eb) & flag) == flag;
+ }
+@@ -2891,7 +2894,7 @@ static inline int btrfs_clear_header_fla
+       return (flags & flag) == flag;
+ }
+-static inline int btrfs_header_backref_rev(struct extent_buffer *eb)
++static inline int btrfs_header_backref_rev(const struct extent_buffer *eb)
+ {
+       u64 flags = btrfs_header_flags(eb);
+       return flags >> BTRFS_BACKREF_REV_SHIFT;
+@@ -2911,12 +2914,12 @@ static inline unsigned long btrfs_header
+       return offsetof(struct btrfs_header, fsid);
+ }
+-static inline unsigned long btrfs_header_chunk_tree_uuid(struct extent_buffer *eb)
++static inline unsigned long btrfs_header_chunk_tree_uuid(const struct extent_buffer *eb)
+ {
+       return offsetof(struct btrfs_header, chunk_tree_uuid);
+ }
+-static inline int btrfs_is_leaf(struct extent_buffer *eb)
++static inline int btrfs_is_leaf(const struct extent_buffer *eb)
+ {
+       return btrfs_header_level(eb) == 0;
+ }
+@@ -2950,12 +2953,12 @@ BTRFS_SETGET_STACK_FUNCS(root_stransid,
+ BTRFS_SETGET_STACK_FUNCS(root_rtransid, struct btrfs_root_item,
+                        rtransid, 64);
+-static inline bool btrfs_root_readonly(struct btrfs_root *root)
++static inline bool btrfs_root_readonly(const struct btrfs_root *root)
+ {
+       return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_RDONLY)) != 0;
+ }
+-static inline bool btrfs_root_dead(struct btrfs_root *root)
++static inline bool btrfs_root_dead(const struct btrfs_root *root)
+ {
+       return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_DEAD)) != 0;
+ }
+@@ -3012,51 +3015,51 @@ BTRFS_SETGET_STACK_FUNCS(backup_num_devi
+ /* struct btrfs_balance_item */
+ BTRFS_SETGET_FUNCS(balance_flags, struct btrfs_balance_item, flags, 64);
+-static inline void btrfs_balance_data(struct extent_buffer *eb,
+-                                    struct btrfs_balance_item *bi,
++static inline void btrfs_balance_data(const struct extent_buffer *eb,
++                                    const struct btrfs_balance_item *bi,
+                                     struct btrfs_disk_balance_args *ba)
+ {
+       read_eb_member(eb, bi, struct btrfs_balance_item, data, ba);
+ }
+ static inline void btrfs_set_balance_data(struct extent_buffer *eb,
+-                                        struct btrfs_balance_item *bi,
+-                                        struct btrfs_disk_balance_args *ba)
++                                struct btrfs_balance_item *bi,
++                                const struct btrfs_disk_balance_args *ba)
+ {
+       write_eb_member(eb, bi, struct btrfs_balance_item, data, ba);
+ }
+-static inline void btrfs_balance_meta(struct extent_buffer *eb,
+-                                    struct btrfs_balance_item *bi,
++static inline void btrfs_balance_meta(const struct extent_buffer *eb,
++                                    const struct btrfs_balance_item *bi,
+                                     struct btrfs_disk_balance_args *ba)
+ {
+       read_eb_member(eb, bi, struct btrfs_balance_item, meta, ba);
+ }
+ static inline void btrfs_set_balance_meta(struct extent_buffer *eb,
+-                                        struct btrfs_balance_item *bi,
+-                                        struct btrfs_disk_balance_args *ba)
++                                struct btrfs_balance_item *bi,
++                                const struct btrfs_disk_balance_args *ba)
+ {
+       write_eb_member(eb, bi, struct btrfs_balance_item, meta, ba);
+ }
+-static inline void btrfs_balance_sys(struct extent_buffer *eb,
+-                                   struct btrfs_balance_item *bi,
++static inline void btrfs_balance_sys(const struct extent_buffer *eb,
++                                   const struct btrfs_balance_item *bi,
+                                    struct btrfs_disk_balance_args *ba)
+ {
+       read_eb_member(eb, bi, struct btrfs_balance_item, sys, ba);
+ }
+ static inline void btrfs_set_balance_sys(struct extent_buffer *eb,
+-                                       struct btrfs_balance_item *bi,
+-                                       struct btrfs_disk_balance_args *ba)
++                               struct btrfs_balance_item *bi,
++                               const struct btrfs_disk_balance_args *ba)
+ {
+       write_eb_member(eb, bi, struct btrfs_balance_item, sys, ba);
+ }
+ static inline void
+ btrfs_disk_balance_args_to_cpu(struct btrfs_balance_args *cpu,
+-                             struct btrfs_disk_balance_args *disk)
++                             const struct btrfs_disk_balance_args *disk)
+ {
+       memset(cpu, 0, sizeof(*cpu));
+@@ -3076,7 +3079,7 @@ btrfs_disk_balance_args_to_cpu(struct bt
+ static inline void
+ btrfs_cpu_balance_args_to_disk(struct btrfs_disk_balance_args *disk,
+-                             struct btrfs_balance_args *cpu)
++                             const struct btrfs_balance_args *cpu)
+ {
+       memset(disk, 0, sizeof(*disk));
+@@ -3144,7 +3147,7 @@ BTRFS_SETGET_STACK_FUNCS(super_magic, st
+ BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block,
+                        uuid_tree_generation, 64);
+-static inline int btrfs_super_csum_size(struct btrfs_super_block *s)
++static inline int btrfs_super_csum_size(const struct btrfs_super_block *s)
+ {
+       u16 t = btrfs_super_csum_type(s);
+       /*
+@@ -3163,8 +3166,8 @@ static inline unsigned long btrfs_leaf_d
+  * this returns the address of the start of the last item,
+  * which is the stop of the leaf data stack
+  */
+-static inline unsigned int leaf_data_end(struct btrfs_root *root,
+-                                       struct extent_buffer *leaf)
++static inline unsigned int leaf_data_end(const struct btrfs_root *root,
++                                       const struct extent_buffer *leaf)
+ {
+       u32 nr = btrfs_header_nritems(leaf);
+@@ -3189,7 +3192,7 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_exte
+                        struct btrfs_file_extent_item, compression, 8);
+ static inline unsigned long
+-btrfs_file_extent_inline_start(struct btrfs_file_extent_item *e)
++btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e)
+ {
+       return (unsigned long)e + BTRFS_FILE_EXTENT_INLINE_DATA_START;
+ }
+@@ -3223,8 +3226,9 @@ BTRFS_SETGET_FUNCS(file_extent_other_enc
+  * size of any extent headers.  If a file is compressed on disk, this is
+  * the compressed size
+  */
+-static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb,
+-                                                  struct btrfs_item *e)
++static inline u32 btrfs_file_extent_inline_item_len(
++                                              const struct extent_buffer *eb,
++                                              struct btrfs_item *e)
+ {
+       return btrfs_item_size(eb, e) - BTRFS_FILE_EXTENT_INLINE_DATA_START;
+ }
+@@ -3232,9 +3236,9 @@ static inline u32 btrfs_file_extent_inli
+ /* this returns the number of file bytes represented by the inline item.
+  * If an item is compressed, this is the uncompressed size
+  */
+-static inline u32 btrfs_file_extent_inline_len(struct extent_buffer *eb,
+-                                             int slot,
+-                                             struct btrfs_file_extent_item *fi)
++static inline u32 btrfs_file_extent_inline_len(const struct extent_buffer *eb,
++                                      int slot,
++                                      const struct btrfs_file_extent_item *fi)
+ {
+       struct btrfs_map_token token;
+@@ -3256,8 +3260,8 @@ static inline u32 btrfs_file_extent_inli
+ /* btrfs_dev_stats_item */
+-static inline u64 btrfs_dev_stats_value(struct extent_buffer *eb,
+-                                      struct btrfs_dev_stats_item *ptr,
++static inline u64 btrfs_dev_stats_value(const struct extent_buffer *eb,
++                                      const struct btrfs_dev_stats_item *ptr,
+                                       int index)
+ {
+       u64 val;
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -5381,9 +5381,8 @@ unlock_exit:
+       return ret;
+ }
+-void read_extent_buffer(struct extent_buffer *eb, void *dstv,
+-                      unsigned long start,
+-                      unsigned long len)
++void read_extent_buffer(const struct extent_buffer *eb, void *dstv,
++                      unsigned long start, unsigned long len)
+ {
+       size_t cur;
+       size_t offset;
+@@ -5412,9 +5411,9 @@ void read_extent_buffer(struct extent_bu
+       }
+ }
+-int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
+-                      unsigned long start,
+-                      unsigned long len)
++int read_extent_buffer_to_user(const struct extent_buffer *eb,
++                             void __user *dstv,
++                             unsigned long start, unsigned long len)
+ {
+       size_t cur;
+       size_t offset;
+@@ -5449,10 +5448,10 @@ int read_extent_buffer_to_user(struct ex
+       return ret;
+ }
+-int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
+-                             unsigned long min_len, char **map,
+-                             unsigned long *map_start,
+-                             unsigned long *map_len)
++int map_private_extent_buffer(const struct extent_buffer *eb,
++                            unsigned long start, unsigned long min_len,
++                            char **map, unsigned long *map_start,
++                            unsigned long *map_len)
+ {
+       size_t offset = start & (PAGE_CACHE_SIZE - 1);
+       char *kaddr;
+@@ -5487,9 +5486,8 @@ int map_private_extent_buffer(struct ext
+       return 0;
+ }
+-int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
+-                        unsigned long start,
+-                        unsigned long len)
++int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
++                       unsigned long start, unsigned long len)
+ {
+       size_t cur;
+       size_t offset;
+--- a/fs/btrfs/extent_io.h
++++ b/fs/btrfs/extent_io.h
+@@ -308,14 +308,13 @@ static inline void extent_buffer_get(str
+       atomic_inc(&eb->refs);
+ }
+-int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
+-                        unsigned long start,
+-                        unsigned long len);
+-void read_extent_buffer(struct extent_buffer *eb, void *dst,
++int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
++                       unsigned long start, unsigned long len);
++void read_extent_buffer(const struct extent_buffer *eb, void *dst,
+                       unsigned long start,
+                       unsigned long len);
+-int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dst,
+-                             unsigned long start,
++int read_extent_buffer_to_user(const struct extent_buffer *eb,
++                             void __user *dst, unsigned long start,
+                              unsigned long len);
+ void write_extent_buffer(struct extent_buffer *eb, const void *src,
+                        unsigned long start, unsigned long len);
+@@ -334,10 +333,10 @@ int set_extent_buffer_uptodate(struct ex
+ int clear_extent_buffer_uptodate(struct extent_buffer *eb);
+ int extent_buffer_uptodate(struct extent_buffer *eb);
+ int extent_buffer_under_io(struct extent_buffer *eb);
+-int map_private_extent_buffer(struct extent_buffer *eb, unsigned long offset,
+-                    unsigned long min_len, char **map,
+-                    unsigned long *map_start,
+-                    unsigned long *map_len);
++int map_private_extent_buffer(const struct extent_buffer *eb,
++                            unsigned long offset, unsigned long min_len,
++                            char **map, unsigned long *map_start,
++                            unsigned long *map_len);
+ int extent_range_clear_dirty_for_io(struct inode *inode, u64 start, u64 end);
+ int extent_range_redirty_for_io(struct inode *inode, u64 start, u64 end);
+ int extent_clear_unlock_delalloc(struct inode *inode, u64 start, u64 end,
+--- a/fs/btrfs/struct-funcs.c
++++ b/fs/btrfs/struct-funcs.c
+@@ -50,8 +50,8 @@ static inline void put_unaligned_le8(u8
+  */
+ #define DEFINE_BTRFS_SETGET_BITS(bits)                                        \
+-u##bits btrfs_get_token_##bits(struct extent_buffer *eb, void *ptr,   \
+-                             unsigned long off,                       \
++u##bits btrfs_get_token_##bits(const struct extent_buffer *eb,                \
++                             const void *ptr, unsigned long off,      \
+                              struct btrfs_map_token *token)           \
+ {                                                                     \
+       unsigned long part_offset = (unsigned long)ptr;                 \
+@@ -90,7 +90,8 @@ u##bits btrfs_get_token_##bits(struct ex
+       return res;                                                     \
+ }                                                                     \
+ void btrfs_set_token_##bits(struct extent_buffer *eb,                 \
+-                          void *ptr, unsigned long off, u##bits val,  \
++                          const void *ptr, unsigned long off,         \
++                          u##bits val,                                \
+                           struct btrfs_map_token *token)              \
+ {                                                                     \
+       unsigned long part_offset = (unsigned long)ptr;                 \
+@@ -133,7 +134,7 @@ DEFINE_BTRFS_SETGET_BITS(16)
+ DEFINE_BTRFS_SETGET_BITS(32)
+ DEFINE_BTRFS_SETGET_BITS(64)
+-void btrfs_node_key(struct extent_buffer *eb,
++void btrfs_node_key(const struct extent_buffer *eb,
+                   struct btrfs_disk_key *disk_key, int nr)
+ {
+       unsigned long ptr = btrfs_node_key_ptr_offset(nr);
diff --git a/queue-4.4/btrfs-tree-check-reduce-stack-consumption-in-check_dir_item.patch b/queue-4.4/btrfs-tree-check-reduce-stack-consumption-in-check_dir_item.patch
new file mode 100644 (file)
index 0000000..2433e22
--- /dev/null
@@ -0,0 +1,49 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: David Sterba <dsterba@suse.com>
+Date: Wed, 10 Jan 2018 15:13:07 +0100
+Subject: btrfs: tree-check: reduce stack consumption in check_dir_item
+
+From: David Sterba <dsterba@suse.com>
+
+commit e2683fc9d219430f5b78889b50cde7f40efeba7b upstream.
+
+I've noticed that the updated item checker stack consumption increased
+dramatically in 542f5385e20cf97447 ("btrfs: tree-checker: Add checker
+for dir item")
+
+tree-checker.c:check_leaf                    +552 (176 -> 728)
+
+The array is 255 bytes long, dynamic allocation would slow down the
+sanity checks so it's more reasonable to keep it on-stack. Moving the
+variable to the scope of use reduces the stack usage again
+
+tree-checker.c:check_leaf                    -264 (728 -> 464)
+
+Reviewed-by: Josef Bacik <jbacik@fb.com>
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -212,7 +212,6 @@ static int check_dir_item(struct btrfs_r
+       di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+       while (cur < item_size) {
+-              char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)];
+               u32 name_len;
+               u32 data_len;
+               u32 max_name_len;
+@@ -295,6 +294,8 @@ static int check_dir_item(struct btrfs_r
+                */
+               if (key->type == BTRFS_DIR_ITEM_KEY ||
+                   key->type == BTRFS_XATTR_ITEM_KEY) {
++                      char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)];
++
+                       read_extent_buffer(leaf, namebuf,
+                                       (unsigned long)(di + 1), name_len);
+                       name_hash = btrfs_name_hash(namebuf, name_len);
diff --git a/queue-4.4/btrfs-tree-checker-add-checker-for-dir-item.patch b/queue-4.4/btrfs-tree-checker-add-checker-for-dir-item.patch
new file mode 100644 (file)
index 0000000..5959fac
--- /dev/null
@@ -0,0 +1,209 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Wed, 8 Nov 2017 08:54:25 +0800
+Subject: btrfs: tree-checker: Add checker for dir item
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit ad7b0368f33cffe67fecd302028915926e50ef7e upstream.
+
+Add checker for dir item, for key types DIR_ITEM, DIR_INDEX and
+XATTR_ITEM.
+
+This checker does comprehensive checks for:
+
+1) dir_item header and its data size
+   Against item boundary and maximum name/xattr length.
+   This part is mostly the same as old verify_dir_item().
+
+2) dir_type
+   Against maximum file types, and against key type.
+   Since XATTR key should only have FT_XATTR dir item, and normal dir
+   item type should not have XATTR key.
+
+   The check between key->type and dir_type is newly introduced by this
+   patch.
+
+3) name hash
+   For XATTR and DIR_ITEM key, key->offset is name hash (crc32c).
+   Check the hash of the name against the key to ensure it's correct.
+
+   The name hash check is only found in btrfs-progs before this patch.
+
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: Su Yue <suy.fnst@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: BTRFS_MAX_XATTR_SIZE() takes a root instead of an
+ fs_info, and yields a value of type size_t instead of unsigned int]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |  141 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 141 insertions(+)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -30,6 +30,7 @@
+ #include "tree-checker.h"
+ #include "disk-io.h"
+ #include "compression.h"
++#include "hash.h"
+ #define CORRUPT(reason, eb, root, slot)                                       \
+       btrfs_crit(root->fs_info,                                       \
+@@ -176,6 +177,141 @@ static int check_csum_item(struct btrfs_
+ }
+ /*
++ * Customized reported for dir_item, only important new info is key->objectid,
++ * which represents inode number
++ */
++__printf(4, 5)
++static void dir_item_err(const struct btrfs_root *root,
++                       const struct extent_buffer *eb, int slot,
++                       const char *fmt, ...)
++{
++      struct btrfs_key key;
++      struct va_format vaf;
++      va_list args;
++
++      btrfs_item_key_to_cpu(eb, &key, slot);
++      va_start(args, fmt);
++
++      vaf.fmt = fmt;
++      vaf.va = &args;
++
++      btrfs_crit(root->fs_info,
++      "corrupt %s: root=%llu block=%llu slot=%d ino=%llu, %pV",
++              btrfs_header_level(eb) == 0 ? "leaf" : "node", root->objectid,
++              btrfs_header_bytenr(eb), slot, key.objectid, &vaf);
++      va_end(args);
++}
++
++static int check_dir_item(struct btrfs_root *root,
++                        struct extent_buffer *leaf,
++                        struct btrfs_key *key, int slot)
++{
++      struct btrfs_dir_item *di;
++      u32 item_size = btrfs_item_size_nr(leaf, slot);
++      u32 cur = 0;
++
++      di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
++      while (cur < item_size) {
++              char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)];
++              u32 name_len;
++              u32 data_len;
++              u32 max_name_len;
++              u32 total_size;
++              u32 name_hash;
++              u8 dir_type;
++
++              /* header itself should not cross item boundary */
++              if (cur + sizeof(*di) > item_size) {
++                      dir_item_err(root, leaf, slot,
++              "dir item header crosses item boundary, have %lu boundary %u",
++                              cur + sizeof(*di), item_size);
++                      return -EUCLEAN;
++              }
++
++              /* dir type check */
++              dir_type = btrfs_dir_type(leaf, di);
++              if (dir_type >= BTRFS_FT_MAX) {
++                      dir_item_err(root, leaf, slot,
++                      "invalid dir item type, have %u expect [0, %u)",
++                              dir_type, BTRFS_FT_MAX);
++                      return -EUCLEAN;
++              }
++
++              if (key->type == BTRFS_XATTR_ITEM_KEY &&
++                  dir_type != BTRFS_FT_XATTR) {
++                      dir_item_err(root, leaf, slot,
++              "invalid dir item type for XATTR key, have %u expect %u",
++                              dir_type, BTRFS_FT_XATTR);
++                      return -EUCLEAN;
++              }
++              if (dir_type == BTRFS_FT_XATTR &&
++                  key->type != BTRFS_XATTR_ITEM_KEY) {
++                      dir_item_err(root, leaf, slot,
++                      "xattr dir type found for non-XATTR key");
++                      return -EUCLEAN;
++              }
++              if (dir_type == BTRFS_FT_XATTR)
++                      max_name_len = XATTR_NAME_MAX;
++              else
++                      max_name_len = BTRFS_NAME_LEN;
++
++              /* Name/data length check */
++              name_len = btrfs_dir_name_len(leaf, di);
++              data_len = btrfs_dir_data_len(leaf, di);
++              if (name_len > max_name_len) {
++                      dir_item_err(root, leaf, slot,
++                      "dir item name len too long, have %u max %u",
++                              name_len, max_name_len);
++                      return -EUCLEAN;
++              }
++              if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(root)) {
++                      dir_item_err(root, leaf, slot,
++                      "dir item name and data len too long, have %u max %zu",
++                              name_len + data_len,
++                              BTRFS_MAX_XATTR_SIZE(root));
++                      return -EUCLEAN;
++              }
++
++              if (data_len && dir_type != BTRFS_FT_XATTR) {
++                      dir_item_err(root, leaf, slot,
++                      "dir item with invalid data len, have %u expect 0",
++                              data_len);
++                      return -EUCLEAN;
++              }
++
++              total_size = sizeof(*di) + name_len + data_len;
++
++              /* header and name/data should not cross item boundary */
++              if (cur + total_size > item_size) {
++                      dir_item_err(root, leaf, slot,
++              "dir item data crosses item boundary, have %u boundary %u",
++                              cur + total_size, item_size);
++                      return -EUCLEAN;
++              }
++
++              /*
++               * Special check for XATTR/DIR_ITEM, as key->offset is name
++               * hash, should match its name
++               */
++              if (key->type == BTRFS_DIR_ITEM_KEY ||
++                  key->type == BTRFS_XATTR_ITEM_KEY) {
++                      read_extent_buffer(leaf, namebuf,
++                                      (unsigned long)(di + 1), name_len);
++                      name_hash = btrfs_name_hash(namebuf, name_len);
++                      if (key->offset != name_hash) {
++                              dir_item_err(root, leaf, slot,
++              "name hash mismatch with key, have 0x%016x expect 0x%016llx",
++                                      name_hash, key->offset);
++                              return -EUCLEAN;
++                      }
++              }
++              cur += total_size;
++              di = (struct btrfs_dir_item *)((void *)di + total_size);
++      }
++      return 0;
++}
++
++/*
+  * Common point to switch the item-specific validation.
+  */
+ static int check_leaf_item(struct btrfs_root *root,
+@@ -191,6 +327,11 @@ static int check_leaf_item(struct btrfs_
+       case BTRFS_EXTENT_CSUM_KEY:
+               ret = check_csum_item(root, leaf, key, slot);
+               break;
++      case BTRFS_DIR_ITEM_KEY:
++      case BTRFS_DIR_INDEX_KEY:
++      case BTRFS_XATTR_ITEM_KEY:
++              ret = check_dir_item(root, leaf, key, slot);
++              break;
+       }
+       return ret;
+ }
diff --git a/queue-4.4/btrfs-tree-checker-check-level-for-leaves-and-nodes.patch b/queue-4.4/btrfs-tree-checker-check-level-for-leaves-and-nodes.patch
new file mode 100644 (file)
index 0000000..36e455e
--- /dev/null
@@ -0,0 +1,63 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Fri, 28 Sep 2018 07:59:34 +0800
+Subject: btrfs: tree-checker: Check level for leaves and nodes
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit f556faa46eb4e96d0d0772e74ecf66781e132f72 upstream.
+
+Although we have tree level check at tree read runtime, it's completely
+based on its parent level.
+We still need to do accurate level check to avoid invalid tree blocks
+sneak into kernel space.
+
+The check itself is simple, for leaf its level should always be 0.
+For nodes its level should be in range [1, BTRFS_MAX_LEVEL - 1].
+
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Su Yue <suy.fnst@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4:
+ - Pass root instead of fs_info to generic_err()
+ - Adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |   14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -447,6 +447,13 @@ static int check_leaf(struct btrfs_root
+       u32 nritems = btrfs_header_nritems(leaf);
+       int slot;
++      if (btrfs_header_level(leaf) != 0) {
++              generic_err(root, leaf, 0,
++                      "invalid level for leaf, have %d expect 0",
++                      btrfs_header_level(leaf));
++              return -EUCLEAN;
++      }
++
+       /*
+        * Extent buffers from a relocation tree have a owner field that
+        * corresponds to the subvolume tree they are based on. So just from an
+@@ -589,9 +596,16 @@ int btrfs_check_node(struct btrfs_root *
+       unsigned long nr = btrfs_header_nritems(node);
+       struct btrfs_key key, next_key;
+       int slot;
++      int level = btrfs_header_level(node);
+       u64 bytenr;
+       int ret = 0;
++      if (level <= 0 || level >= BTRFS_MAX_LEVEL) {
++              generic_err(root, node, 0,
++                      "invalid level for node, have %d expect [1, %d]",
++                      level, BTRFS_MAX_LEVEL - 1);
++              return -EUCLEAN;
++      }
+       if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
+               btrfs_crit(root->fs_info,
+ "corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%zu]",
diff --git a/queue-4.4/btrfs-tree-checker-detect-invalid-and-empty-essential-trees.patch b/queue-4.4/btrfs-tree-checker-detect-invalid-and-empty-essential-trees.patch
new file mode 100644 (file)
index 0000000..cb4ef48
--- /dev/null
@@ -0,0 +1,71 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Tue, 3 Jul 2018 17:10:06 +0800
+Subject: btrfs: tree-checker: Detect invalid and empty essential trees
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit ba480dd4db9f1798541eb2d1c423fc95feee8d36 upstream.
+
+A crafted image has empty root tree block, which will later cause NULL
+pointer dereference.
+
+The following trees should never be empty:
+1) Tree root
+   Must contain at least root items for extent tree, device tree and fs
+   tree
+
+2) Chunk tree
+   Or we can't even bootstrap as it contains the mapping.
+
+3) Fs tree
+   At least inode item for top level inode (.).
+
+4) Device tree
+   Dev extents for chunks
+
+5) Extent tree
+   Must have corresponding extent for each chunk.
+
+If any of them is empty, we are sure the fs is corrupted and no need to
+mount it.
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=199847
+Reported-by: Xu Wen <wen.xu@gatech.edu>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Tested-by: Gu Jinxiang <gujx@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: Pass root instead of fs_info to generic_err()]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |   15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -456,9 +456,22 @@ static int check_leaf(struct btrfs_root
+        * skip this check for relocation trees.
+        */
+       if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) {
++              u64 owner = btrfs_header_owner(leaf);
+               struct btrfs_root *check_root;
+-              key.objectid = btrfs_header_owner(leaf);
++              /* These trees must never be empty */
++              if (owner == BTRFS_ROOT_TREE_OBJECTID ||
++                  owner == BTRFS_CHUNK_TREE_OBJECTID ||
++                  owner == BTRFS_EXTENT_TREE_OBJECTID ||
++                  owner == BTRFS_DEV_TREE_OBJECTID ||
++                  owner == BTRFS_FS_TREE_OBJECTID ||
++                  owner == BTRFS_DATA_RELOC_TREE_OBJECTID) {
++                      generic_err(root, leaf, 0,
++                      "invalid root, root %llu must never be empty",
++                                  owner);
++                      return -EUCLEAN;
++              }
++              key.objectid = owner;
+               key.type = BTRFS_ROOT_ITEM_KEY;
+               key.offset = (u64)-1;
diff --git a/queue-4.4/btrfs-tree-checker-enhance-btrfs_check_node-output.patch b/queue-4.4/btrfs-tree-checker-enhance-btrfs_check_node-output.patch
new file mode 100644 (file)
index 0000000..43fcf4e
--- /dev/null
@@ -0,0 +1,131 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+Date: Mon, 9 Oct 2017 01:51:03 +0000
+Subject: btrfs: tree-checker: Enhance btrfs_check_node output
+
+From: Qu Wenruo <quwenruo.btrfs@gmx.com>
+
+commit bba4f29896c986c4cec17bc0f19f2ce644fceae1 upstream.
+
+Use inline function to replace macro since we don't need
+stringification.
+(Macro still exists until all callers get updated)
+
+And add more info about the error, and replace EIO with EUCLEAN.
+
+For nr_items error, report if it's too large or too small, and output
+the valid value range.
+
+For node block pointer, added a new alignment checker.
+
+For key order, also output the next key to make the problem more
+obvious.
+
+Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
+[ wording adjustments, unindented long strings ]
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4:
+ - Use root->sectorsize instead of root->fs_info->sectorsize
+ - BTRFS_NODEPTRS_PER_BLOCK() takes a root instead of an fs_info, and yields
+   a value of type size_t instead of unsigned int]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |   68 +++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 61 insertions(+), 7 deletions(-)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -37,6 +37,46 @@
+                  btrfs_header_level(eb) == 0 ? "leaf" : "node",       \
+                  reason, btrfs_header_bytenr(eb), root->objectid, slot)
++/*
++ * Error message should follow the following format:
++ * corrupt <type>: <identifier>, <reason>[, <bad_value>]
++ *
++ * @type:     leaf or node
++ * @identifier:       the necessary info to locate the leaf/node.
++ *            It's recommened to decode key.objecitd/offset if it's
++ *            meaningful.
++ * @reason:   describe the error
++ * @bad_value:        optional, it's recommened to output bad value and its
++ *            expected value (range).
++ *
++ * Since comma is used to separate the components, only space is allowed
++ * inside each component.
++ */
++
++/*
++ * Append generic "corrupt leaf/node root=%llu block=%llu slot=%d: " to @fmt.
++ * Allows callers to customize the output.
++ */
++__printf(4, 5)
++static void generic_err(const struct btrfs_root *root,
++                      const struct extent_buffer *eb, int slot,
++                      const char *fmt, ...)
++{
++      struct va_format vaf;
++      va_list args;
++
++      va_start(args, fmt);
++
++      vaf.fmt = fmt;
++      vaf.va = &args;
++
++      btrfs_crit(root->fs_info,
++              "corrupt %s: root=%llu block=%llu slot=%d, %pV",
++              btrfs_header_level(eb) == 0 ? "leaf" : "node",
++              root->objectid, btrfs_header_bytenr(eb), slot, &vaf);
++      va_end(args);
++}
++
+ static int check_extent_data_item(struct btrfs_root *root,
+                                 struct extent_buffer *leaf,
+                                 struct btrfs_key *key, int slot)
+@@ -282,9 +322,11 @@ int btrfs_check_node(struct btrfs_root *
+       if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root)) {
+               btrfs_crit(root->fs_info,
+-                         "corrupt node: block %llu root %llu nritems %lu",
+-                         node->start, root->objectid, nr);
+-              return -EIO;
++"corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%zu]",
++                         root->objectid, node->start,
++                         nr == 0 ? "small" : "large", nr,
++                         BTRFS_NODEPTRS_PER_BLOCK(root));
++              return -EUCLEAN;
+       }
+       for (slot = 0; slot < nr - 1; slot++) {
+@@ -293,14 +335,26 @@ int btrfs_check_node(struct btrfs_root *
+               btrfs_node_key_to_cpu(node, &next_key, slot + 1);
+               if (!bytenr) {
+-                      CORRUPT("invalid item slot", node, root, slot);
+-                      ret = -EIO;
++                      generic_err(root, node, slot,
++                              "invalid NULL node pointer");
++                      ret = -EUCLEAN;
++                      goto out;
++              }
++              if (!IS_ALIGNED(bytenr, root->sectorsize)) {
++                      generic_err(root, node, slot,
++                      "unaligned pointer, have %llu should be aligned to %u",
++                              bytenr, root->sectorsize);
++                      ret = -EUCLEAN;
+                       goto out;
+               }
+               if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
+-                      CORRUPT("bad key order", node, root, slot);
+-                      ret = -EIO;
++                      generic_err(root, node, slot,
++      "bad key order, current (%llu %u %llu) next (%llu %u %llu)",
++                              key.objectid, key.type, key.offset,
++                              next_key.objectid, next_key.type,
++                              next_key.offset);
++                      ret = -EUCLEAN;
+                       goto out;
+               }
+       }
diff --git a/queue-4.4/btrfs-tree-checker-fix-false-panic-for-sanity-test.patch b/queue-4.4/btrfs-tree-checker-fix-false-panic-for-sanity-test.patch
new file mode 100644 (file)
index 0000000..630b347
--- /dev/null
@@ -0,0 +1,162 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Wed, 8 Nov 2017 08:54:24 +0800
+Subject: btrfs: tree-checker: Fix false panic for sanity test
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 69fc6cbbac542c349b3d350d10f6e394c253c81d upstream.
+
+[BUG]
+If we run btrfs with CONFIG_BTRFS_FS_RUN_SANITY_TESTS=y, it will
+instantly cause kernel panic like:
+
+------
+...
+assertion failed: 0, file: fs/btrfs/disk-io.c, line: 3853
+...
+Call Trace:
+ btrfs_mark_buffer_dirty+0x187/0x1f0 [btrfs]
+ setup_items_for_insert+0x385/0x650 [btrfs]
+ __btrfs_drop_extents+0x129a/0x1870 [btrfs]
+...
+-----
+
+[Cause]
+Btrfs will call btrfs_check_leaf() in btrfs_mark_buffer_dirty() to check
+if the leaf is valid with CONFIG_BTRFS_FS_RUN_SANITY_TESTS=y.
+
+However quite some btrfs_mark_buffer_dirty() callers(*) don't really
+initialize its item data but only initialize its item pointers, leaving
+item data uninitialized.
+
+This makes tree-checker catch uninitialized data as error, causing
+such panic.
+
+*: These callers include but not limited to
+setup_items_for_insert()
+btrfs_split_item()
+btrfs_expand_item()
+
+[Fix]
+Add a new parameter @check_item_data to btrfs_check_leaf().
+With @check_item_data set to false, item data check will be skipped and
+fallback to old btrfs_check_leaf() behavior.
+
+So we can still get early warning if we screw up item pointers, and
+avoid false panic.
+
+Cc: Filipe Manana <fdmanana@gmail.com>
+Reported-by: Lakshmipathi.G <lakshmipathi.g@gmail.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/disk-io.c      |   10 ++++++++--
+ fs/btrfs/tree-checker.c |   27 ++++++++++++++++++++++-----
+ fs/btrfs/tree-checker.h |   14 +++++++++++++-
+ 3 files changed, 43 insertions(+), 8 deletions(-)
+
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -589,7 +589,7 @@ static int btree_readpage_end_io_hook(st
+        * that we don't try and read the other copies of this block, just
+        * return -EIO.
+        */
+-      if (found_level == 0 && btrfs_check_leaf(root, eb)) {
++      if (found_level == 0 && btrfs_check_leaf_full(root, eb)) {
+               set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags);
+               ret = -EIO;
+       }
+@@ -3896,7 +3896,13 @@ void btrfs_mark_buffer_dirty(struct exte
+                                    buf->len,
+                                    root->fs_info->dirty_metadata_batch);
+ #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+-      if (btrfs_header_level(buf) == 0 && btrfs_check_leaf(root, buf)) {
++      /*
++       * Since btrfs_mark_buffer_dirty() can be called with item pointer set
++       * but item data not updated.
++       * So here we should only check item pointers, not item data.
++       */
++      if (btrfs_header_level(buf) == 0 &&
++          btrfs_check_leaf_relaxed(root, buf)) {
+               btrfs_print_leaf(root, buf);
+               ASSERT(0);
+       }
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -195,7 +195,8 @@ static int check_leaf_item(struct btrfs_
+       return ret;
+ }
+-int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf)
++static int check_leaf(struct btrfs_root *root, struct extent_buffer *leaf,
++                    bool check_item_data)
+ {
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       /* No valid key type is 0, so all key should be larger than this key */
+@@ -299,10 +300,15 @@ int btrfs_check_leaf(struct btrfs_root *
+                       return -EUCLEAN;
+               }
+-              /* Check if the item size and content meet other criteria */
+-              ret = check_leaf_item(root, leaf, &key, slot);
+-              if (ret < 0)
+-                      return ret;
++              if (check_item_data) {
++                      /*
++                       * Check if the item size and content meet other
++                       * criteria
++                       */
++                      ret = check_leaf_item(root, leaf, &key, slot);
++                      if (ret < 0)
++                              return ret;
++              }
+               prev_key.objectid = key.objectid;
+               prev_key.type = key.type;
+@@ -312,6 +318,17 @@ int btrfs_check_leaf(struct btrfs_root *
+       return 0;
+ }
++int btrfs_check_leaf_full(struct btrfs_root *root, struct extent_buffer *leaf)
++{
++      return check_leaf(root, leaf, true);
++}
++
++int btrfs_check_leaf_relaxed(struct btrfs_root *root,
++                           struct extent_buffer *leaf)
++{
++      return check_leaf(root, leaf, false);
++}
++
+ int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node)
+ {
+       unsigned long nr = btrfs_header_nritems(node);
+--- a/fs/btrfs/tree-checker.h
++++ b/fs/btrfs/tree-checker.h
+@@ -20,7 +20,19 @@
+ #include "ctree.h"
+ #include "extent_io.h"
+-int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf);
++/*
++ * Comprehensive leaf checker.
++ * Will check not only the item pointers, but also every possible member
++ * in item data.
++ */
++int btrfs_check_leaf_full(struct btrfs_root *root, struct extent_buffer *leaf);
++
++/*
++ * Less strict leaf checker.
++ * Will only check item pointers, not reading item data.
++ */
++int btrfs_check_leaf_relaxed(struct btrfs_root *root,
++                           struct extent_buffer *leaf);
+ int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node);
+ #endif
diff --git a/queue-4.4/btrfs-tree-checker-fix-misleading-group-system-information.patch b/queue-4.4/btrfs-tree-checker-fix-misleading-group-system-information.patch
new file mode 100644 (file)
index 0000000..7888c90
--- /dev/null
@@ -0,0 +1,37 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Shaokun Zhang <zhangshaokun@hisilicon.com>
+Date: Mon, 5 Nov 2018 18:49:09 +0800
+Subject: btrfs: tree-checker: Fix misleading group system information
+
+From: Shaokun Zhang <zhangshaokun@hisilicon.com>
+
+commit 761333f2f50ccc887aa9957ae829300262c0d15b upstream.
+
+block_group_err shows the group system as a decimal value with a '0x'
+prefix, which is somewhat misleading.
+
+Fix it to print hexadecimal, as was intended.
+
+Fixes: fce466eab7ac6 ("btrfs: tree-checker: Verify block_group_item")
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -399,7 +399,7 @@ static int check_block_group_item(struct
+           type != (BTRFS_BLOCK_GROUP_METADATA |
+                          BTRFS_BLOCK_GROUP_DATA)) {
+               block_group_err(fs_info, leaf, slot,
+-"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llu or 0x%llx",
++"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llx or 0x%llx",
+                       type, hweight64(type),
+                       BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA,
+                       BTRFS_BLOCK_GROUP_SYSTEM,
diff --git a/queue-4.4/btrfs-tree-checker-use-zu-format-string-for-size_t.patch b/queue-4.4/btrfs-tree-checker-use-zu-format-string-for-size_t.patch
new file mode 100644 (file)
index 0000000..75fc6d1
--- /dev/null
@@ -0,0 +1,37 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Wed, 6 Dec 2017 15:18:14 +0100
+Subject: btrfs: tree-checker: use %zu format string for size_t
+
+From: Arnd Bergmann <arnd@arndb.de>
+
+commit 7cfad65297bfe0aa2996cd72d21c898aa84436d9 upstream.
+
+The return value of sizeof() is of type size_t, so we must print it
+using the %z format modifier rather than %l to avoid this warning
+on some architectures:
+
+fs/btrfs/tree-checker.c: In function 'check_dir_item':
+fs/btrfs/tree-checker.c:273:50: error: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'u32' {aka 'unsigned int'} [-Werror=format=]
+
+Fixes: 005887f2e3e0 ("btrfs: tree-checker: Add checker for dir item")
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -223,7 +223,7 @@ static int check_dir_item(struct btrfs_r
+               /* header itself should not cross item boundary */
+               if (cur + sizeof(*di) > item_size) {
+                       dir_item_err(root, leaf, slot,
+-              "dir item header crosses item boundary, have %lu boundary %u",
++              "dir item header crosses item boundary, have %zu boundary %u",
+                               cur + sizeof(*di), item_size);
+                       return -EUCLEAN;
+               }
diff --git a/queue-4.4/btrfs-tree-checker-verify-block_group_item.patch b/queue-4.4/btrfs-tree-checker-verify-block_group_item.patch
new file mode 100644 (file)
index 0000000..ebdf8c7
--- /dev/null
@@ -0,0 +1,203 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Tue, 3 Jul 2018 17:10:05 +0800
+Subject: btrfs: tree-checker: Verify block_group_item
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit fce466eab7ac6baa9d2dcd88abcf945be3d4a089 upstream.
+
+A crafted image with invalid block group items could make free space cache
+code to cause panic.
+
+We could detect such invalid block group item by checking:
+1) Item size
+   Known fixed value.
+2) Block group size (key.offset)
+   We have an upper limit on block group item (10G)
+3) Chunk objectid
+   Known fixed value.
+4) Type
+   Only 4 valid type values, DATA, METADATA, SYSTEM and DATA|METADATA.
+   No more than 1 bit set for profile type.
+5) Used space
+   No more than the block group size.
+
+This should allow btrfs to detect and refuse to mount the crafted image.
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=199849
+Reported-by: Xu Wen <wen.xu@gatech.edu>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Gu Jinxiang <gujx@cn.fujitsu.com>
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Tested-by: Gu Jinxiang <gujx@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4:
+ - In check_leaf_item(), pass root->fs_info to check_block_group_item()
+ - Include <linux/sizes.h> (in ctree.h, to match upstream)
+ - Adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/ctree.h        |    1 
+ fs/btrfs/tree-checker.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++++
+ fs/btrfs/volumes.c      |    2 
+ fs/btrfs/volumes.h      |    2 
+ 4 files changed, 104 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/ctree.h
++++ b/fs/btrfs/ctree.h
+@@ -35,6 +35,7 @@
+ #include <linux/btrfs.h>
+ #include <linux/workqueue.h>
+ #include <linux/security.h>
++#include <linux/sizes.h>
+ #include "extent_io.h"
+ #include "extent_map.h"
+ #include "async-thread.h"
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -31,6 +31,7 @@
+ #include "disk-io.h"
+ #include "compression.h"
+ #include "hash.h"
++#include "volumes.h"
+ #define CORRUPT(reason, eb, root, slot)                                       \
+       btrfs_crit(root->fs_info,                                       \
+@@ -312,6 +313,102 @@ static int check_dir_item(struct btrfs_r
+       return 0;
+ }
++__printf(4, 5)
++__cold
++static void block_group_err(const struct btrfs_fs_info *fs_info,
++                          const struct extent_buffer *eb, int slot,
++                          const char *fmt, ...)
++{
++      struct btrfs_key key;
++      struct va_format vaf;
++      va_list args;
++
++      btrfs_item_key_to_cpu(eb, &key, slot);
++      va_start(args, fmt);
++
++      vaf.fmt = fmt;
++      vaf.va = &args;
++
++      btrfs_crit(fs_info,
++      "corrupt %s: root=%llu block=%llu slot=%d bg_start=%llu bg_len=%llu, %pV",
++              btrfs_header_level(eb) == 0 ? "leaf" : "node",
++              btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot,
++              key.objectid, key.offset, &vaf);
++      va_end(args);
++}
++
++static int check_block_group_item(struct btrfs_fs_info *fs_info,
++                                struct extent_buffer *leaf,
++                                struct btrfs_key *key, int slot)
++{
++      struct btrfs_block_group_item bgi;
++      u32 item_size = btrfs_item_size_nr(leaf, slot);
++      u64 flags;
++      u64 type;
++
++      /*
++       * Here we don't really care about alignment since extent allocator can
++       * handle it.  We care more about the size, as if one block group is
++       * larger than maximum size, it's must be some obvious corruption.
++       */
++      if (key->offset > BTRFS_MAX_DATA_CHUNK_SIZE || key->offset == 0) {
++              block_group_err(fs_info, leaf, slot,
++                      "invalid block group size, have %llu expect (0, %llu]",
++                              key->offset, BTRFS_MAX_DATA_CHUNK_SIZE);
++              return -EUCLEAN;
++      }
++
++      if (item_size != sizeof(bgi)) {
++              block_group_err(fs_info, leaf, slot,
++                      "invalid item size, have %u expect %zu",
++                              item_size, sizeof(bgi));
++              return -EUCLEAN;
++      }
++
++      read_extent_buffer(leaf, &bgi, btrfs_item_ptr_offset(leaf, slot),
++                         sizeof(bgi));
++      if (btrfs_block_group_chunk_objectid(&bgi) !=
++          BTRFS_FIRST_CHUNK_TREE_OBJECTID) {
++              block_group_err(fs_info, leaf, slot,
++              "invalid block group chunk objectid, have %llu expect %llu",
++                              btrfs_block_group_chunk_objectid(&bgi),
++                              BTRFS_FIRST_CHUNK_TREE_OBJECTID);
++              return -EUCLEAN;
++      }
++
++      if (btrfs_block_group_used(&bgi) > key->offset) {
++              block_group_err(fs_info, leaf, slot,
++                      "invalid block group used, have %llu expect [0, %llu)",
++                              btrfs_block_group_used(&bgi), key->offset);
++              return -EUCLEAN;
++      }
++
++      flags = btrfs_block_group_flags(&bgi);
++      if (hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) > 1) {
++              block_group_err(fs_info, leaf, slot,
++"invalid profile flags, have 0x%llx (%lu bits set) expect no more than 1 bit set",
++                      flags & BTRFS_BLOCK_GROUP_PROFILE_MASK,
++                      hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK));
++              return -EUCLEAN;
++      }
++
++      type = flags & BTRFS_BLOCK_GROUP_TYPE_MASK;
++      if (type != BTRFS_BLOCK_GROUP_DATA &&
++          type != BTRFS_BLOCK_GROUP_METADATA &&
++          type != BTRFS_BLOCK_GROUP_SYSTEM &&
++          type != (BTRFS_BLOCK_GROUP_METADATA |
++                         BTRFS_BLOCK_GROUP_DATA)) {
++              block_group_err(fs_info, leaf, slot,
++"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llu or 0x%llx",
++                      type, hweight64(type),
++                      BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA,
++                      BTRFS_BLOCK_GROUP_SYSTEM,
++                      BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA);
++              return -EUCLEAN;
++      }
++      return 0;
++}
++
+ /*
+  * Common point to switch the item-specific validation.
+  */
+@@ -333,6 +430,9 @@ static int check_leaf_item(struct btrfs_
+       case BTRFS_XATTR_ITEM_KEY:
+               ret = check_dir_item(root, leaf, key, slot);
+               break;
++      case BTRFS_BLOCK_GROUP_ITEM_KEY:
++              ret = check_block_group_item(root->fs_info, leaf, key, slot);
++              break;
+       }
+       return ret;
+ }
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -4540,7 +4540,7 @@ static int __btrfs_alloc_chunk(struct bt
+       if (type & BTRFS_BLOCK_GROUP_DATA) {
+               max_stripe_size = 1024 * 1024 * 1024;
+-              max_chunk_size = 10 * max_stripe_size;
++              max_chunk_size = BTRFS_MAX_DATA_CHUNK_SIZE;
+               if (!devs_max)
+                       devs_max = BTRFS_MAX_DEVS(info->chunk_root);
+       } else if (type & BTRFS_BLOCK_GROUP_METADATA) {
+--- a/fs/btrfs/volumes.h
++++ b/fs/btrfs/volumes.h
+@@ -24,6 +24,8 @@
+ #include <linux/btrfs.h>
+ #include "async-thread.h"
++#define BTRFS_MAX_DATA_CHUNK_SIZE     (10ULL * SZ_1G)
++
+ extern struct mutex uuid_mutex;
+ #define BTRFS_STRIPE_LEN      (64 * 1024)
diff --git a/queue-4.4/btrfs-validate-type-when-reading-a-chunk.patch b/queue-4.4/btrfs-validate-type-when-reading-a-chunk.patch
new file mode 100644 (file)
index 0000000..1294b90
--- /dev/null
@@ -0,0 +1,71 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Gu Jinxiang <gujx@cn.fujitsu.com>
+Date: Wed, 4 Jul 2018 18:16:39 +0800
+Subject: btrfs: validate type when reading a chunk
+
+From: Gu Jinxiang <gujx@cn.fujitsu.com>
+
+commit 315409b0098fb2651d86553f0436b70502b29bb2 upstream.
+
+Reported in https://bugzilla.kernel.org/show_bug.cgi?id=199839, with an
+image that has an invalid chunk type but does not return an error.
+
+Add chunk type check in btrfs_check_chunk_valid, to detect the wrong
+type combinations.
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=199839
+Reported-by: Xu Wen <wen.xu@gatech.edu>
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Gu Jinxiang <gujx@cn.fujitsu.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: Use root->fs_info instead of fs_info]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/volumes.c |   28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -6218,6 +6218,8 @@ static int btrfs_check_chunk_valid(struc
+       u16 num_stripes;
+       u16 sub_stripes;
+       u64 type;
++      u64 features;
++      bool mixed = false;
+       length = btrfs_chunk_length(leaf, chunk);
+       stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+@@ -6258,6 +6260,32 @@ static int btrfs_check_chunk_valid(struc
+                         btrfs_chunk_type(leaf, chunk));
+               return -EIO;
+       }
++
++      if ((type & BTRFS_BLOCK_GROUP_TYPE_MASK) == 0) {
++              btrfs_err(root->fs_info, "missing chunk type flag: 0x%llx", type);
++              return -EIO;
++      }
++
++      if ((type & BTRFS_BLOCK_GROUP_SYSTEM) &&
++          (type & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA))) {
++              btrfs_err(root->fs_info,
++                      "system chunk with data or metadata type: 0x%llx", type);
++              return -EIO;
++      }
++
++      features = btrfs_super_incompat_flags(root->fs_info->super_copy);
++      if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
++              mixed = true;
++
++      if (!mixed) {
++              if ((type & BTRFS_BLOCK_GROUP_METADATA) &&
++                  (type & BTRFS_BLOCK_GROUP_DATA)) {
++                      btrfs_err(root->fs_info,
++                      "mixed chunk type in non-mixed mode: 0x%llx", type);
++                      return -EIO;
++              }
++      }
++
+       if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) ||
+           (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
+           (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
diff --git a/queue-4.4/btrfs-verify-that-every-chunk-has-corresponding-block-group-at-mount-time.patch b/queue-4.4/btrfs-verify-that-every-chunk-has-corresponding-block-group-at-mount-time.patch
new file mode 100644 (file)
index 0000000..c665cda
--- /dev/null
@@ -0,0 +1,106 @@
+From foo@baz Mon Jan 14 14:39:22 CET 2019
+From: Qu Wenruo <wqu@suse.com>
+Date: Wed, 1 Aug 2018 10:37:17 +0800
+Subject: btrfs: Verify that every chunk has corresponding block group at mount time
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 7ef49515fa6727cb4b6f2f5b0ffbc5fc20a9f8c6 upstream.
+
+If a crafted image has missing block group items, it could cause
+unexpected behavior and breaks the assumption of 1:1 chunk<->block group
+mapping.
+
+Although we have the block group -> chunk mapping check, we still need
+chunk -> block group mapping check.
+
+This patch will do extra check to ensure each chunk has its
+corresponding block group.
+
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=199847
+Reported-by: Xu Wen <wen.xu@gatech.edu>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Gu Jinxiang <gujx@cn.fujitsu.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[bwh: Backported to 4.4: adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent-tree.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 57 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/extent-tree.c
++++ b/fs/btrfs/extent-tree.c
+@@ -9765,6 +9765,62 @@ btrfs_create_block_group_cache(struct bt
+       return cache;
+ }
++
++/*
++ * Iterate all chunks and verify that each of them has the corresponding block
++ * group
++ */
++static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info)
++{
++      struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
++      struct extent_map *em;
++      struct btrfs_block_group_cache *bg;
++      u64 start = 0;
++      int ret = 0;
++
++      while (1) {
++              read_lock(&map_tree->map_tree.lock);
++              /*
++               * lookup_extent_mapping will return the first extent map
++               * intersecting the range, so setting @len to 1 is enough to
++               * get the first chunk.
++               */
++              em = lookup_extent_mapping(&map_tree->map_tree, start, 1);
++              read_unlock(&map_tree->map_tree.lock);
++              if (!em)
++                      break;
++
++              bg = btrfs_lookup_block_group(fs_info, em->start);
++              if (!bg) {
++                      btrfs_err(fs_info,
++      "chunk start=%llu len=%llu doesn't have corresponding block group",
++                                   em->start, em->len);
++                      ret = -EUCLEAN;
++                      free_extent_map(em);
++                      break;
++              }
++              if (bg->key.objectid != em->start ||
++                  bg->key.offset != em->len ||
++                  (bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK) !=
++                  (em->map_lookup->type & BTRFS_BLOCK_GROUP_TYPE_MASK)) {
++                      btrfs_err(fs_info,
++"chunk start=%llu len=%llu flags=0x%llx doesn't match block group start=%llu len=%llu flags=0x%llx",
++                              em->start, em->len,
++                              em->map_lookup->type & BTRFS_BLOCK_GROUP_TYPE_MASK,
++                              bg->key.objectid, bg->key.offset,
++                              bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK);
++                      ret = -EUCLEAN;
++                      free_extent_map(em);
++                      btrfs_put_block_group(bg);
++                      break;
++              }
++              start = em->start + em->len;
++              free_extent_map(em);
++              btrfs_put_block_group(bg);
++      }
++      return ret;
++}
++
+ int btrfs_read_block_groups(struct btrfs_root *root)
+ {
+       struct btrfs_path *path;
+@@ -9951,7 +10007,7 @@ int btrfs_read_block_groups(struct btrfs
+       }
+       init_global_block_rsv(info);
+-      ret = 0;
++      ret = check_chunk_block_group_mappings(info);
+ error:
+       btrfs_free_path(path);
+       return ret;
index 1a112bbcb1ea50151f8d084faec571fb69d3c0b6..59f5fb0147a1a6396177d32b1536d648be2eb778 100644 (file)
@@ -1 +1,32 @@
 alsa-hda-realtek-disable-headset-mic-vref-for-headset-mode-of-alc225.patch
+btrfs-cleanup-stop-casting-for-extent_map-lookup-everywhere.patch
+btrfs-enhance-chunk-validation-check.patch
+btrfs-add-validadtion-checks-for-chunk-loading.patch
+btrfs-check-inconsistence-between-chunk-and-block-group.patch
+btrfs-fix-em-leak-in-find_first_block_group.patch
+btrfs-detect-corruption-when-non-root-leaf-has-zero-item.patch
+btrfs-check-btree-node-s-nritems.patch
+btrfs-fix-bug_on-in-btrfs_mark_buffer_dirty.patch
+btrfs-memset-to-avoid-stale-content-in-btree-node-block.patch
+btrfs-improve-check_node-to-avoid-reading-corrupted-nodes.patch
+btrfs-kill-bug_on-in-run_delayed_tree_ref.patch
+btrfs-memset-to-avoid-stale-content-in-btree-leaf.patch
+btrfs-fix-emptiness-check-for-dirtied-extent-buffers-at-check_leaf.patch
+btrfs-struct-funcs-constify-readers.patch
+btrfs-refactor-check_leaf-function-for-later-expansion.patch
+btrfs-check-if-item-pointer-overlaps-with-the-item-itself.patch
+btrfs-add-sanity-check-for-extent_data-when-reading-out-leaf.patch
+btrfs-add-checker-for-extent_csum.patch
+btrfs-move-leaf-and-node-validation-checker-to-tree-checker.c.patch
+btrfs-tree-checker-enhance-btrfs_check_node-output.patch
+btrfs-tree-checker-fix-false-panic-for-sanity-test.patch
+btrfs-tree-checker-add-checker-for-dir-item.patch
+btrfs-tree-checker-use-zu-format-string-for-size_t.patch
+btrfs-tree-check-reduce-stack-consumption-in-check_dir_item.patch
+btrfs-tree-checker-verify-block_group_item.patch
+btrfs-tree-checker-detect-invalid-and-empty-essential-trees.patch
+btrfs-validate-type-when-reading-a-chunk.patch
+btrfs-check-that-each-block-group-has-corresponding-chunk-at-mount-time.patch
+btrfs-verify-that-every-chunk-has-corresponding-block-group-at-mount-time.patch
+btrfs-tree-checker-check-level-for-leaves-and-nodes.patch
+btrfs-tree-checker-fix-misleading-group-system-information.patch