From 803e00b965649ead25eca7b3b6613b7e73ebde90 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Aug 2020 13:42:13 +0200 Subject: [PATCH] 5.8-stable patches added patches: btrfs-check-correct-variable-after-allocation-in-btrfs_backref_iter_alloc.patch btrfs-don-t-show-full-path-of-bind-mounts-in-subvol.patch btrfs-fix-memory-leaks-after-failure-to-lookup-checksums-during-inode-logging.patch btrfs-fix-messages-after-changing-compression-level-by-remount.patch btrfs-fix-return-value-mixup-in-btrfs_get_extent.patch btrfs-inode-fix-null-pointer-dereference-if-inode-doesn-t-need-compression.patch btrfs-make-sure-sb_i_version-doesn-t-get-unset-by-remount.patch btrfs-only-search-for-left_info-if-there-is-no-right_info-in-try_merge_free_space.patch btrfs-trim-fix-underflow-in-trim-length-to-prevent-access-beyond-device-boundary.patch --- ...location-in-btrfs_backref_iter_alloc.patch | 36 ++++++ ...w-full-path-of-bind-mounts-in-subvol.patch | 63 ++++++++++ ...ookup-checksums-during-inode-logging.patch | 54 +++++++++ ...hanging-compression-level-by-remount.patch | 84 +++++++++++++ ...turn-value-mixup-in-btrfs_get_extent.patch | 35 ++++++ ...ce-if-inode-doesn-t-need-compression.patch | 105 ++++++++++++++++ ...version-doesn-t-get-unset-by-remount.patch | 44 +++++++ ...o-right_info-in-try_merge_free_space.patch | 64 ++++++++++ ...revent-access-beyond-device-boundary.patch | 114 ++++++++++++++++++ queue-5.8/series | 9 ++ 10 files changed, 608 insertions(+) create mode 100644 queue-5.8/btrfs-check-correct-variable-after-allocation-in-btrfs_backref_iter_alloc.patch create mode 100644 queue-5.8/btrfs-don-t-show-full-path-of-bind-mounts-in-subvol.patch create mode 100644 queue-5.8/btrfs-fix-memory-leaks-after-failure-to-lookup-checksums-during-inode-logging.patch create mode 100644 queue-5.8/btrfs-fix-messages-after-changing-compression-level-by-remount.patch create mode 100644 queue-5.8/btrfs-fix-return-value-mixup-in-btrfs_get_extent.patch create mode 100644 queue-5.8/btrfs-inode-fix-null-pointer-dereference-if-inode-doesn-t-need-compression.patch create mode 100644 queue-5.8/btrfs-make-sure-sb_i_version-doesn-t-get-unset-by-remount.patch create mode 100644 queue-5.8/btrfs-only-search-for-left_info-if-there-is-no-right_info-in-try_merge_free_space.patch create mode 100644 queue-5.8/btrfs-trim-fix-underflow-in-trim-length-to-prevent-access-beyond-device-boundary.patch diff --git a/queue-5.8/btrfs-check-correct-variable-after-allocation-in-btrfs_backref_iter_alloc.patch b/queue-5.8/btrfs-check-correct-variable-after-allocation-in-btrfs_backref_iter_alloc.patch new file mode 100644 index 00000000000..e514b6b9653 --- /dev/null +++ b/queue-5.8/btrfs-check-correct-variable-after-allocation-in-btrfs_backref_iter_alloc.patch @@ -0,0 +1,36 @@ +From c15c2ec07a26b251040943a1a9f90d3037f041e5 Mon Sep 17 00:00:00 2001 +From: Boleyn Su +Date: Thu, 6 Aug 2020 15:31:44 +0900 +Subject: btrfs: check correct variable after allocation in btrfs_backref_iter_alloc + +From: Boleyn Su + +commit c15c2ec07a26b251040943a1a9f90d3037f041e5 upstream. + +The `if (!ret)` check will always be false and it may result in +ret->path being dereferenced while it is a NULL pointer. + +Fixes: a37f232b7b65 ("btrfs: backref: introduce the skeleton of btrfs_backref_iter") +CC: stable@vger.kernel.org # 5.8+ +Reviewed-by: Nikolay Borisov +Reviewed-by: Qu Wenruo +Signed-off-by: Boleyn Su +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/backref.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/btrfs/backref.c ++++ b/fs/btrfs/backref.c +@@ -2303,7 +2303,7 @@ struct btrfs_backref_iter *btrfs_backref + return NULL; + + ret->path = btrfs_alloc_path(); +- if (!ret) { ++ if (!ret->path) { + kfree(ret); + return NULL; + } diff --git a/queue-5.8/btrfs-don-t-show-full-path-of-bind-mounts-in-subvol.patch b/queue-5.8/btrfs-don-t-show-full-path-of-bind-mounts-in-subvol.patch new file mode 100644 index 00000000000..6940a38d868 --- /dev/null +++ b/queue-5.8/btrfs-don-t-show-full-path-of-bind-mounts-in-subvol.patch @@ -0,0 +1,63 @@ +From 3ef3959b29c4a5bd65526ab310a1a18ae533172a Mon Sep 17 00:00:00 2001 +From: Josef Bacik +Date: Wed, 22 Jul 2020 11:12:46 -0400 +Subject: btrfs: don't show full path of bind mounts in subvol= + +From: Josef Bacik + +commit 3ef3959b29c4a5bd65526ab310a1a18ae533172a upstream. + +Chris Murphy reported a problem where rpm ostree will bind mount a bunch +of things for whatever voodoo it's doing. But when it does this +/proc/mounts shows something like + + /dev/sda /mnt/test btrfs rw,relatime,subvolid=256,subvol=/foo 0 0 + /dev/sda /mnt/test/baz btrfs rw,relatime,subvolid=256,subvol=/foo/bar 0 0 + +Despite subvolid=256 being subvol=/foo. This is because we're just +spitting out the dentry of the mount point, which in the case of bind +mounts is the source path for the mountpoint. Instead we should spit +out the path to the actual subvol. Fix this by looking up the name for +the subvolid we have mounted. With this fix the same test looks like +this + + /dev/sda /mnt/test btrfs rw,relatime,subvolid=256,subvol=/foo 0 0 + /dev/sda /mnt/test/baz btrfs rw,relatime,subvolid=256,subvol=/foo 0 0 + +Reported-by: Chris Murphy +CC: stable@vger.kernel.org # 4.4+ +Signed-off-by: Josef Bacik +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/super.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -1312,6 +1312,7 @@ static int btrfs_show_options(struct seq + { + struct btrfs_fs_info *info = btrfs_sb(dentry->d_sb); + const char *compress_type; ++ const char *subvol_name; + + if (btrfs_test_opt(info, DEGRADED)) + seq_puts(seq, ",degraded"); +@@ -1398,8 +1399,13 @@ static int btrfs_show_options(struct seq + seq_puts(seq, ",ref_verify"); + seq_printf(seq, ",subvolid=%llu", + BTRFS_I(d_inode(dentry))->root->root_key.objectid); +- seq_puts(seq, ",subvol="); +- seq_dentry(seq, dentry, " \t\n\\"); ++ subvol_name = btrfs_get_subvol_name_from_objectid(info, ++ BTRFS_I(d_inode(dentry))->root->root_key.objectid); ++ if (!IS_ERR(subvol_name)) { ++ seq_puts(seq, ",subvol="); ++ seq_escape(seq, subvol_name, " \t\n\\"); ++ kfree(subvol_name); ++ } + return 0; + } + diff --git a/queue-5.8/btrfs-fix-memory-leaks-after-failure-to-lookup-checksums-during-inode-logging.patch b/queue-5.8/btrfs-fix-memory-leaks-after-failure-to-lookup-checksums-during-inode-logging.patch new file mode 100644 index 00000000000..fbc4f748573 --- /dev/null +++ b/queue-5.8/btrfs-fix-memory-leaks-after-failure-to-lookup-checksums-during-inode-logging.patch @@ -0,0 +1,54 @@ +From 4f26433e9b3eb7a55ed70d8f882ae9cd48ba448b Mon Sep 17 00:00:00 2001 +From: Filipe Manana +Date: Wed, 29 Jul 2020 10:17:50 +0100 +Subject: btrfs: fix memory leaks after failure to lookup checksums during inode logging + +From: Filipe Manana + +commit 4f26433e9b3eb7a55ed70d8f882ae9cd48ba448b upstream. + +While logging an inode, at copy_items(), if we fail to lookup the checksums +for an extent we release the destination path, free the ins_data array and +then return immediately. However a previous iteration of the for loop may +have added checksums to the ordered_sums list, in which case we leak the +memory used by them. + +So fix this by making sure we iterate the ordered_sums list and free all +its checksums before returning. + +Fixes: 3650860b90cc2a ("Btrfs: remove almost all of the BUG()'s from tree-log.c") +CC: stable@vger.kernel.org # 4.4+ +Reviewed-by: Johannes Thumshirn +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/tree-log.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -4027,11 +4027,8 @@ static noinline int copy_items(struct bt + fs_info->csum_root, + ds + cs, ds + cs + cl - 1, + &ordered_sums, 0); +- if (ret) { +- btrfs_release_path(dst_path); +- kfree(ins_data); +- return ret; +- } ++ if (ret) ++ break; + } + } + } +@@ -4044,7 +4041,6 @@ static noinline int copy_items(struct bt + * we have to do this after the loop above to avoid changing the + * log tree while trying to change the log tree. + */ +- ret = 0; + while (!list_empty(&ordered_sums)) { + struct btrfs_ordered_sum *sums = list_entry(ordered_sums.next, + struct btrfs_ordered_sum, diff --git a/queue-5.8/btrfs-fix-messages-after-changing-compression-level-by-remount.patch b/queue-5.8/btrfs-fix-messages-after-changing-compression-level-by-remount.patch new file mode 100644 index 00000000000..2238497960b --- /dev/null +++ b/queue-5.8/btrfs-fix-messages-after-changing-compression-level-by-remount.patch @@ -0,0 +1,84 @@ +From 27942c9971cc405c60432eca9395e514a2ae9f5e Mon Sep 17 00:00:00 2001 +From: David Sterba +Date: Thu, 23 Jul 2020 19:08:55 +0200 +Subject: btrfs: fix messages after changing compression level by remount + +From: David Sterba + +commit 27942c9971cc405c60432eca9395e514a2ae9f5e upstream. + +Reported by Forza on IRC that remounting with compression options does +not reflect the change in level, or at least it does not appear to do so +according to the messages: + + mount -o compress=zstd:1 /dev/sda /mnt + mount -o remount,compress=zstd:15 /mnt + +does not print the change to the level to syslog: + + [ 41.366060] BTRFS info (device vda): use zstd compression, level 1 + [ 41.368254] BTRFS info (device vda): disk space caching is enabled + [ 41.390429] BTRFS info (device vda): disk space caching is enabled + +What really happens is that the message is lost but the level is actualy +changed. + +There's another weird output, if compression is reset to 'no': + + [ 45.413776] BTRFS info (device vda): use no compression, level 4 + +To fix that, save the previous compression level and print the message +in that case too and use separate message for 'no' compression. + +CC: stable@vger.kernel.org # 4.19+ +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/super.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -449,6 +449,7 @@ int btrfs_parse_options(struct btrfs_fs_ + char *compress_type; + bool compress_force = false; + enum btrfs_compression_type saved_compress_type; ++ int saved_compress_level; + bool saved_compress_force; + int no_compress = 0; + +@@ -531,6 +532,7 @@ int btrfs_parse_options(struct btrfs_fs_ + info->compress_type : BTRFS_COMPRESS_NONE; + saved_compress_force = + btrfs_test_opt(info, FORCE_COMPRESS); ++ saved_compress_level = info->compress_level; + if (token == Opt_compress || + token == Opt_compress_force || + strncmp(args[0].from, "zlib", 4) == 0) { +@@ -575,6 +577,8 @@ int btrfs_parse_options(struct btrfs_fs_ + no_compress = 0; + } else if (strncmp(args[0].from, "no", 2) == 0) { + compress_type = "no"; ++ info->compress_level = 0; ++ info->compress_type = 0; + btrfs_clear_opt(info->mount_opt, COMPRESS); + btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS); + compress_force = false; +@@ -595,11 +599,11 @@ int btrfs_parse_options(struct btrfs_fs_ + */ + btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS); + } +- if ((btrfs_test_opt(info, COMPRESS) && +- (info->compress_type != saved_compress_type || +- compress_force != saved_compress_force)) || +- (!btrfs_test_opt(info, COMPRESS) && +- no_compress == 1)) { ++ if (no_compress == 1) { ++ btrfs_info(info, "use no compression"); ++ } else if ((info->compress_type != saved_compress_type) || ++ (compress_force != saved_compress_force) || ++ (info->compress_level != saved_compress_level)) { + btrfs_info(info, "%s %s compression, level %d", + (compress_force) ? "force" : "use", + compress_type, info->compress_level); diff --git a/queue-5.8/btrfs-fix-return-value-mixup-in-btrfs_get_extent.patch b/queue-5.8/btrfs-fix-return-value-mixup-in-btrfs_get_extent.patch new file mode 100644 index 00000000000..4084bf21653 --- /dev/null +++ b/queue-5.8/btrfs-fix-return-value-mixup-in-btrfs_get_extent.patch @@ -0,0 +1,35 @@ +From 881a3a11c2b858fe9b69ef79ac5ee9978a266dc9 Mon Sep 17 00:00:00 2001 +From: Pavel Machek +Date: Mon, 3 Aug 2020 11:35:06 +0200 +Subject: btrfs: fix return value mixup in btrfs_get_extent + +From: Pavel Machek + +commit 881a3a11c2b858fe9b69ef79ac5ee9978a266dc9 upstream. + +btrfs_get_extent() sets variable ret, but out: error path expect error +to be in variable err so the error code is lost. + +Fixes: 6bf9e4bd6a27 ("btrfs: inode: Verify inode mode to avoid NULL pointer dereference") +CC: stable@vger.kernel.org # 5.4+ +Reviewed-by: Nikolay Borisov +Signed-off-by: Pavel Machek (CIP) +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/inode.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -6633,7 +6633,7 @@ struct extent_map *btrfs_get_extent(stru + extent_type == BTRFS_FILE_EXTENT_PREALLOC) { + /* Only regular file could have regular/prealloc extent */ + if (!S_ISREG(inode->vfs_inode.i_mode)) { +- ret = -EUCLEAN; ++ err = -EUCLEAN; + btrfs_crit(fs_info, + "regular/prealloc extent found for non-regular inode %llu", + btrfs_ino(inode)); diff --git a/queue-5.8/btrfs-inode-fix-null-pointer-dereference-if-inode-doesn-t-need-compression.patch b/queue-5.8/btrfs-inode-fix-null-pointer-dereference-if-inode-doesn-t-need-compression.patch new file mode 100644 index 00000000000..93ebbbec6fd --- /dev/null +++ b/queue-5.8/btrfs-inode-fix-null-pointer-dereference-if-inode-doesn-t-need-compression.patch @@ -0,0 +1,105 @@ +From 1e6e238c3002ea3611465ce5f32777ddd6a40126 Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Tue, 28 Jul 2020 16:39:26 +0800 +Subject: btrfs: inode: fix NULL pointer dereference if inode doesn't need compression + +From: Qu Wenruo + +commit 1e6e238c3002ea3611465ce5f32777ddd6a40126 upstream. + +[BUG] +There is a bug report of NULL pointer dereference caused in +compress_file_extent(): + + Oops: Kernel access of bad area, sig: 11 [#1] + LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries + Workqueue: btrfs-delalloc btrfs_delalloc_helper [btrfs] + NIP [c008000006dd4d34] compress_file_range.constprop.41+0x75c/0x8a0 [btrfs] + LR [c008000006dd4d1c] compress_file_range.constprop.41+0x744/0x8a0 [btrfs] + Call Trace: + [c000000c69093b00] [c008000006dd4d1c] compress_file_range.constprop.41+0x744/0x8a0 [btrfs] (unreliable) + [c000000c69093bd0] [c008000006dd4ebc] async_cow_start+0x44/0xa0 [btrfs] + [c000000c69093c10] [c008000006e14824] normal_work_helper+0xdc/0x598 [btrfs] + [c000000c69093c80] [c0000000001608c0] process_one_work+0x2c0/0x5b0 + [c000000c69093d10] [c000000000160c38] worker_thread+0x88/0x660 + [c000000c69093db0] [c00000000016b55c] kthread+0x1ac/0x1c0 + [c000000c69093e20] [c00000000000b660] ret_from_kernel_thread+0x5c/0x7c + ---[ end trace f16954aa20d822f6 ]--- + +[CAUSE] +For the following execution route of compress_file_range(), it's +possible to hit NULL pointer dereference: + + compress_file_extent() + |- pages = NULL; + |- start = async_chunk->start = 0; + |- end = async_chunk = 4095; + |- nr_pages = 1; + |- inode_need_compress() == false; <<< Possible, see later explanation + | Now, we have nr_pages = 1, pages = NULL + |- cont: + |- ret = cow_file_range_inline(); + |- if (ret <= 0) { + |- for (i = 0; i < nr_pages; i++) { + |- WARN_ON(pages[i]->mapping); <<< Crash + +To enter above call execution branch, we need the following race: + + Thread 1 (chattr) | Thread 2 (writeback) +--------------------------+------------------------------ + | btrfs_run_delalloc_range + | |- inode_need_compress = true + | |- cow_file_range_async() +btrfs_ioctl_set_flag() | +|- binode_flags |= | + BTRFS_INODE_NOCOMPRESS | + | compress_file_range() + | |- inode_need_compress = false + | |- nr_page = 1 while pages = NULL + | | Then hit the crash + +[FIX] +This patch will fix it by checking @pages before doing accessing it. +This patch is only designed as a hot fix and easy to backport. + +More elegant fix may make btrfs only check inode_need_compress() once to +avoid such race, but that would be another story. + +Reported-by: Luciano Chavez +Fixes: 4d3a800ebb12 ("btrfs: merge nr_pages input and output parameter in compress_pages") +CC: stable@vger.kernel.org # 4.14.x: cecc8d9038d16: btrfs: Move free_pages_out label in inline extent handling branch in compress_file_range +CC: stable@vger.kernel.org # 4.14+ +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/inode.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -650,12 +650,18 @@ cont: + page_error_op | + PAGE_END_WRITEBACK); + +- for (i = 0; i < nr_pages; i++) { +- WARN_ON(pages[i]->mapping); +- put_page(pages[i]); ++ /* ++ * Ensure we only free the compressed pages if we have ++ * them allocated, as we can still reach here with ++ * inode_need_compress() == false. ++ */ ++ if (pages) { ++ for (i = 0; i < nr_pages; i++) { ++ WARN_ON(pages[i]->mapping); ++ put_page(pages[i]); ++ } ++ kfree(pages); + } +- kfree(pages); +- + return 0; + } + } diff --git a/queue-5.8/btrfs-make-sure-sb_i_version-doesn-t-get-unset-by-remount.patch b/queue-5.8/btrfs-make-sure-sb_i_version-doesn-t-get-unset-by-remount.patch new file mode 100644 index 00000000000..ef75492bd69 --- /dev/null +++ b/queue-5.8/btrfs-make-sure-sb_i_version-doesn-t-get-unset-by-remount.patch @@ -0,0 +1,44 @@ +From faa008899a4db21a2df99833cb4ff6fa67009a20 Mon Sep 17 00:00:00 2001 +From: Josef Bacik +Date: Thu, 30 Jul 2020 11:18:09 -0400 +Subject: btrfs: make sure SB_I_VERSION doesn't get unset by remount + +From: Josef Bacik + +commit faa008899a4db21a2df99833cb4ff6fa67009a20 upstream. + +There's some inconsistency around SB_I_VERSION handling with mount and +remount. Since we don't really want it to be off ever just work around +this by making sure we don't get the flag cleared on remount. + +There's a tiny cpu cost of setting the bit, otherwise all changes to +i_version also change some of the times (ctime/mtime) so the inode needs +to be synced. We wouldn't save anything by disabling it. + +Reported-by: Eric Sandeen +CC: stable@vger.kernel.org # 5.4+ +Signed-off-by: Josef Bacik +Reviewed-by: David Sterba +[ add perf impact analysis ] +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/super.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -1897,6 +1897,12 @@ static int btrfs_remount(struct super_bl + set_bit(BTRFS_FS_OPEN, &fs_info->flags); + } + out: ++ /* ++ * We need to set SB_I_VERSION here otherwise it'll get cleared by VFS, ++ * since the absence of the flag means it can be toggled off by remount. ++ */ ++ *flags |= SB_I_VERSION; ++ + wake_up_process(fs_info->transaction_kthread); + btrfs_remount_cleanup(fs_info, old_opts); + return 0; diff --git a/queue-5.8/btrfs-only-search-for-left_info-if-there-is-no-right_info-in-try_merge_free_space.patch b/queue-5.8/btrfs-only-search-for-left_info-if-there-is-no-right_info-in-try_merge_free_space.patch new file mode 100644 index 00000000000..2269b261aca --- /dev/null +++ b/queue-5.8/btrfs-only-search-for-left_info-if-there-is-no-right_info-in-try_merge_free_space.patch @@ -0,0 +1,64 @@ +From bf53d4687b8f3f6b752f091eb85f62369a515dfd Mon Sep 17 00:00:00 2001 +From: Josef Bacik +Date: Mon, 27 Jul 2020 10:28:05 -0400 +Subject: btrfs: only search for left_info if there is no right_info in try_merge_free_space + +From: Josef Bacik + +commit bf53d4687b8f3f6b752f091eb85f62369a515dfd upstream. + +In try_to_merge_free_space we attempt to find entries to the left and +right of the entry we are adding to see if they can be merged. We +search for an entry past our current info (saved into right_info), and +then if right_info exists and it has a rb_prev() we save the rb_prev() +into left_info. + +However there's a slight problem in the case that we have a right_info, +but no entry previous to that entry. At that point we will search for +an entry just before the info we're attempting to insert. This will +simply find right_info again, and assign it to left_info, making them +both the same pointer. + +Now if right_info _can_ be merged with the range we're inserting, we'll +add it to the info and free right_info. However further down we'll +access left_info, which was right_info, and thus get a use-after-free. + +Fix this by only searching for the left entry if we don't find a right +entry at all. + +The CVE referenced had a specially crafted file system that could +trigger this use-after-free. However with the tree checker improvements +we no longer trigger the conditions for the UAF. But the original +conditions still apply, hence this fix. + +Reference: CVE-2019-19448 +Fixes: 963030817060 ("Btrfs: use hybrid extents+bitmap rb tree for free space") +CC: stable@vger.kernel.org # 4.4+ +Signed-off-by: Josef Bacik +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/free-space-cache.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/fs/btrfs/free-space-cache.c ++++ b/fs/btrfs/free-space-cache.c +@@ -2281,7 +2281,7 @@ out: + static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl, + struct btrfs_free_space *info, bool update_stat) + { +- struct btrfs_free_space *left_info; ++ struct btrfs_free_space *left_info = NULL; + struct btrfs_free_space *right_info; + bool merged = false; + u64 offset = info->offset; +@@ -2297,7 +2297,7 @@ static bool try_merge_free_space(struct + if (right_info && rb_prev(&right_info->offset_index)) + left_info = rb_entry(rb_prev(&right_info->offset_index), + struct btrfs_free_space, offset_index); +- else ++ else if (!right_info) + left_info = tree_search_offset(ctl, offset - 1, 0, 0); + + /* See try_merge_free_space() comment. */ diff --git a/queue-5.8/btrfs-trim-fix-underflow-in-trim-length-to-prevent-access-beyond-device-boundary.patch b/queue-5.8/btrfs-trim-fix-underflow-in-trim-length-to-prevent-access-beyond-device-boundary.patch new file mode 100644 index 00000000000..4b8d7598b81 --- /dev/null +++ b/queue-5.8/btrfs-trim-fix-underflow-in-trim-length-to-prevent-access-beyond-device-boundary.patch @@ -0,0 +1,114 @@ +From c57dd1f2f6a7cd1bb61802344f59ccdc5278c983 Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Fri, 31 Jul 2020 19:29:11 +0800 +Subject: btrfs: trim: fix underflow in trim length to prevent access beyond device boundary + +From: Qu Wenruo + +commit c57dd1f2f6a7cd1bb61802344f59ccdc5278c983 upstream. + +[BUG] +The following script can lead to tons of beyond device boundary access: + + mkfs.btrfs -f $dev -b 10G + mount $dev $mnt + trimfs $mnt + btrfs filesystem resize 1:-1G $mnt + trimfs $mnt + +[CAUSE] +Since commit 929be17a9b49 ("btrfs: Switch btrfs_trim_free_extents to +find_first_clear_extent_bit"), we try to avoid trimming ranges that's +already trimmed. + +So we check device->alloc_state by finding the first range which doesn't +have CHUNK_TRIMMED and CHUNK_ALLOCATED not set. + +But if we shrunk the device, that bits are not cleared, thus we could +easily got a range starts beyond the shrunk device size. + +This results the returned @start and @end are all beyond device size, +then we call "end = min(end, device->total_bytes -1);" making @end +smaller than device size. + +Then finally we goes "len = end - start + 1", totally underflow the +result, and lead to the beyond-device-boundary access. + +[FIX] +This patch will fix the problem in two ways: + +- Clear CHUNK_TRIMMED | CHUNK_ALLOCATED bits when shrinking device + This is the root fix + +- Add extra safety check when trimming free device extents + We check and warn if the returned range is already beyond current + device. + +Link: https://github.com/kdave/btrfs-progs/issues/282 +Fixes: 929be17a9b49 ("btrfs: Switch btrfs_trim_free_extents to find_first_clear_extent_bit") +CC: stable@vger.kernel.org # 5.4+ +Signed-off-by: Qu Wenruo +Reviewed-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/extent-io-tree.h | 2 ++ + fs/btrfs/extent-tree.c | 14 ++++++++++++++ + fs/btrfs/volumes.c | 4 ++++ + 3 files changed, 20 insertions(+) + +--- a/fs/btrfs/extent-io-tree.h ++++ b/fs/btrfs/extent-io-tree.h +@@ -34,6 +34,8 @@ struct io_failure_record; + */ + #define CHUNK_ALLOCATED EXTENT_DIRTY + #define CHUNK_TRIMMED EXTENT_DEFRAG ++#define CHUNK_STATE_MASK (CHUNK_ALLOCATED | \ ++ CHUNK_TRIMMED) + + enum { + IO_TREE_FS_PINNED_EXTENTS, +--- a/fs/btrfs/extent-tree.c ++++ b/fs/btrfs/extent-tree.c +@@ -33,6 +33,7 @@ + #include "delalloc-space.h" + #include "block-group.h" + #include "discard.h" ++#include "rcu-string.h" + + #undef SCRAMBLE_DELAYED_REFS + +@@ -5668,6 +5669,19 @@ static int btrfs_trim_free_extents(struc + &start, &end, + CHUNK_TRIMMED | CHUNK_ALLOCATED); + ++ /* Check if there are any CHUNK_* bits left */ ++ if (start > device->total_bytes) { ++ WARN_ON(IS_ENABLED(CONFIG_BTRFS_DEBUG)); ++ btrfs_warn_in_rcu(fs_info, ++"ignoring attempt to trim beyond device size: offset %llu length %llu device %s device size %llu", ++ start, end - start + 1, ++ rcu_str_deref(device->name), ++ device->total_bytes); ++ mutex_unlock(&fs_info->chunk_mutex); ++ ret = 0; ++ break; ++ } ++ + /* Ensure we skip the reserved area in the first 1M */ + start = max_t(u64, start, SZ_1M); + +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -4720,6 +4720,10 @@ again: + } + + mutex_lock(&fs_info->chunk_mutex); ++ /* Clear all state bits beyond the shrunk device size */ ++ clear_extent_bits(&device->alloc_state, new_size, (u64)-1, ++ CHUNK_STATE_MASK); ++ + btrfs_device_set_disk_total_bytes(device, new_size); + if (list_empty(&device->post_commit_list)) + list_add_tail(&device->post_commit_list, diff --git a/queue-5.8/series b/queue-5.8/series index ba8273426cc..6178089ab7c 100644 --- a/queue-5.8/series +++ b/queue-5.8/series @@ -30,3 +30,12 @@ btrfs-return-erofs-for-btrfs_fs_state_error-cases.patch btrfs-sysfs-use-nofs-for-device-creation.patch btrfs-don-t-warn-if-we-abort-a-transaction-with-erofs.patch btrfs-fix-race-between-page-release-and-a-fast-fsync.patch +btrfs-don-t-show-full-path-of-bind-mounts-in-subvol.patch +btrfs-fix-messages-after-changing-compression-level-by-remount.patch +btrfs-only-search-for-left_info-if-there-is-no-right_info-in-try_merge_free_space.patch +btrfs-inode-fix-null-pointer-dereference-if-inode-doesn-t-need-compression.patch +btrfs-fix-memory-leaks-after-failure-to-lookup-checksums-during-inode-logging.patch +btrfs-trim-fix-underflow-in-trim-length-to-prevent-access-beyond-device-boundary.patch +btrfs-make-sure-sb_i_version-doesn-t-get-unset-by-remount.patch +btrfs-fix-return-value-mixup-in-btrfs_get_extent.patch +btrfs-check-correct-variable-after-allocation-in-btrfs_backref_iter_alloc.patch -- 2.47.3