DO NOT MERGE - f2fs-tools: fix corrupted xattr entry

Detect and fix a corrupted xattr entry.

Bug: 305658663
Signed-off-by: Daeho Jeong <daehojeong@google.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d2f788271e6fb5f3ac1b3de4fac39ec5bad76129)
Merged-In: Ib3c4d67d2fb8523306ca2502834579d7631ae513
Change-Id: Ib3c4d67d2fb8523306ca2502834579d7631ae513
diff --git a/fsck/dump.c b/fsck/dump.c
index 4275f39..0138a18 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -358,7 +358,7 @@
 	char xattr_name[F2FS_NAME_LEN] = {0};
 	int ret;
 
-	xattr = read_all_xattrs(sbi, node_blk);
+	xattr = read_all_xattrs(sbi, node_blk, true);
 	if (!xattr)
 		return;
 
diff --git a/fsck/fsck.c b/fsck/fsck.c
index 324c3d5..4a18ecb 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -685,6 +685,43 @@
 	}
 }
 
+int chk_extended_attributes(struct f2fs_sb_info *sbi, u32 nid,
+		struct f2fs_node *inode)
+{
+	void *xattr;
+	void *last_base_addr;
+	struct f2fs_xattr_entry *ent;
+	__u32 xattr_size = XATTR_SIZE(&inode->i);
+
+	if (xattr_size == 0)
+		return 0;
+
+	xattr = read_all_xattrs(sbi, inode, false);
+	ASSERT(xattr);
+
+	last_base_addr = (void *)xattr + xattr_size;
+
+	list_for_each_xattr(ent, xattr) {
+		if ((void *)(ent) + sizeof(__u32) > last_base_addr ||
+			(void *)XATTR_NEXT_ENTRY(ent) > last_base_addr) {
+			ASSERT_MSG("[0x%x] last xattr entry (offset: %lx) "
+					"crosses the boundary",
+					nid, (long int)((void *)ent - xattr));
+			if (c.fix_on) {
+				memset(ent, 0,
+					(char *)last_base_addr - (char *)ent);
+				write_all_xattrs(sbi, inode, xattr_size, xattr);
+				FIX_MSG("[0x%x] nullify wrong xattr entries",
+						nid);
+				return 1;
+			}
+			break;
+		}
+	}
+
+	return 0;
+}
+
 /* start with valid nid and blkaddr */
 void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk,
@@ -860,6 +897,9 @@
 		}
 	}
 
+	if (chk_extended_attributes(sbi, nid, node_blk))
+		need_fix = 1;
+
 	if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) {
 		unsigned int inline_size = MAX_INLINE_DATA(node_blk);
 		if (cur_qtype != -1)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index dabd8b9..02ccc93 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -328,6 +328,8 @@
 						struct dentry *de);
 
 /* xattr.c */
-void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *);
+void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *, bool);
+void write_all_xattrs(struct f2fs_sb_info *sbi,
+		struct f2fs_node *inode, __u32 hsize, void *txattr_addr);
 
 #endif /* _FSCK_H_ */
diff --git a/fsck/mount.c b/fsck/mount.c
index 055c225..5bdac76 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -334,7 +334,7 @@
 	DISP_u32(inode, i_nid[3]);	/* indirect */
 	DISP_u32(inode, i_nid[4]);	/* double indirect */
 
-	xattr_addr = read_all_xattrs(sbi, node);
+	xattr_addr = read_all_xattrs(sbi, node, true);
 	if (!xattr_addr)
 		goto out;
 
diff --git a/fsck/xattr.c b/fsck/xattr.c
index 863c0b4..a77b2e6 100644
--- a/fsck/xattr.c
+++ b/fsck/xattr.c
@@ -17,14 +17,15 @@
 #include "node.h"
 #include "xattr.h"
 
-void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
+void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
+			bool sanity_check)
 {
 	struct f2fs_xattr_header *header;
 	void *txattr_addr;
 	u64 inline_size = inline_xattr_size(&inode->i);
 	nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
 
-	if (c.func == FSCK && xnid) {
+	if (c.func == FSCK && xnid && sanity_check) {
 		struct f2fs_node *node_blk = NULL;
 		struct node_info ni;
 		int ret;
@@ -88,7 +89,7 @@
 	return entry;
 }
 
-static void write_all_xattrs(struct f2fs_sb_info *sbi,
+void write_all_xattrs(struct f2fs_sb_info *sbi,
 		struct f2fs_node *inode, __u32 hsize, void *txattr_addr)
 {
 	void *xattr_addr;
@@ -175,7 +176,7 @@
 	ret = dev_read_block(inode, ni.blk_addr);
 	ASSERT(ret >= 0);
 
-	base_addr = read_all_xattrs(sbi, inode);
+	base_addr = read_all_xattrs(sbi, inode, true);
 	ASSERT(base_addr);
 
 	last_base_addr = (void *)base_addr + XATTR_SIZE(&inode->i);