All of lore.kernel.org
 help / color / mirror / Atom feed
From: Qu Wenruo <wqu@suse.com>
To: linux-btrfs@vger.kernel.org
Cc: Wang Yugui <wangyugui@e16-tech.com>
Subject: [PATCH] btrfs-progs: fix a lowmem mode crash where fatal error is not properly handled
Date: Mon,  1 Nov 2021 19:30:17 +0800	[thread overview]
Message-ID: <20211101113017.52665-1-wqu@suse.com> (raw)

[BUG]
When a special image (diverted from fsck/012) has its unused slots (slot
number >= nritems) with garbage, lowmem mode btrfs check can crash:

  (gdb) run check --mode=lowmem ~/downloads/good.img.restored
  Starting program: /home/adam/btrfs/btrfs-progs/btrfs check --mode=lowmem ~/downloads/good.img.restored
  ...
  ERROR: root 5 INODE[5044031582654955520] nlink(257228800) not equal to inode_refs(0)
  ERROR: root 5 INODE[5044031582654955520] nbytes 474624 not equal to extent_size 0

  Program received signal SIGSEGV, Segmentation fault.
  0x0000555555639b11 in btrfs_inode_size (eb=0x5555558a7540, s=0x642e6cd1) at ./kernel-shared/ctree.h:1703
  1703	BTRFS_SETGET_FUNCS(inode_size, struct btrfs_inode_item, size, 64);
  (gdb) bt
  #0  0x0000555555639b11 in btrfs_inode_size (eb=0x5555558a7540, s=0x642e6cd1) at ./kernel-shared/ctree.h:1703
  #1  0x0000555555641544 in check_inode_item (root=0x5555556c2290, path=0x7fffffffd960) at check/mode-lowmem.c:2628

[CAUSE]
At check_inode_item() we have path->slot[0] at 29, while the tree block
only has 26 items.

This happens because two reasons:

- btrfs_next_item() never reverts its slots
  Even if we failed to read next leaf.

- check_inode_item() doesn't inform the caller that a fatal error
  happened
  In check_inode_item(), if btrfs_next_item() failed, it goes to out
  label, which doesn't really set @err properly.

This means, when check_inode_item() fails at btrfs_next_item(), it will
increase path->slots[0], while it's already beyond current tree block
nritems.

When the slot increases furthermore, and if the unused item slots have
some garbage, we will get invalid btrfs_item_ptr() result, and causing
above segfault.

[FIX]
Fix the problems by two ways:

- Make btrfs_next_item() to revert its path->slots[0] on failure

- Properly detect fatal error from check_inode_item()

By this, we will no longer crash on the crafted image.

Reported-by: Wang Yugui <wangyugui@e16-tech.com>
Issue: #412
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/mode-lowmem.c   |  4 ++--
 kernel-shared/ctree.h | 13 +++++++++++--
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index b88b2b1980c9..696ad2157c2b 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -2686,7 +2686,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
 			ret = -EIO;
 
 		if (ret < 0) {
-			/* out will fill 'err' rusing current statistics */
+			err |= FATAL_ERROR;
 			goto out;
 		} else if (ret > 0) {
 			err |= LAST_ITEM;
@@ -2898,7 +2898,7 @@ again:
 	/* modify cur since check_inode_item may change path */
 	cur = path->nodes[0];
 
-	if (err & LAST_ITEM)
+	if (err & LAST_ITEM || err & FATAL_ERROR)
 		goto out;
 
 	/* still have inode items in this leaf */
diff --git a/kernel-shared/ctree.h b/kernel-shared/ctree.h
index 563ea50b3587..8af09623f817 100644
--- a/kernel-shared/ctree.h
+++ b/kernel-shared/ctree.h
@@ -2729,8 +2729,17 @@ static inline int btrfs_next_item(struct btrfs_root *root,
 				  struct btrfs_path *p)
 {
 	++p->slots[0];
-	if (p->slots[0] >= btrfs_header_nritems(p->nodes[0]))
-		return btrfs_next_leaf(root, p);
+	if (p->slots[0] >= btrfs_header_nritems(p->nodes[0])) {
+		int ret;
+		ret = btrfs_next_leaf(root, p);
+		/*
+		 * Revert the increased slot, or the path may point to
+		 * an invalid item.
+		 */
+		if (ret)
+			p->slots[0]--;
+		return ret;
+	}
 	return 0;
 }
 
-- 
2.33.1


             reply	other threads:[~2021-11-01 11:30 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-01 11:30 Qu Wenruo [this message]
2021-11-01 19:22 ` [PATCH] btrfs-progs: fix a lowmem mode crash where fatal error is not properly handled David Sterba

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211101113017.52665-1-wqu@suse.com \
    --to=wqu@suse.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=wangyugui@e16-tech.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.