linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	stable@vger.kernel.org, Chao Yu <yuchao0@huawei.com>,
	Gao Xiang <hsiangkao@redhat.com>
Subject: [PATCH 4.19 01/15] erofs: fix extended inode could cross boundary
Date: Wed,  5 May 2021 14:05:06 +0200	[thread overview]
Message-ID: <20210505120503.831517445@linuxfoundation.org> (raw)
In-Reply-To: <20210505120503.781531508@linuxfoundation.org>

From: Gao Xiang <hsiangkao@redhat.com>

commit 0dcd3c94e02438f4a571690e26f4ee997524102a upstream.

Each ondisk inode should be aligned with inode slot boundary
(32-byte alignment) because of nid calculation formula, so all
compact inodes (32 byte) cannot across page boundary. However,
extended inode is now 64-byte form, which can across page boundary
in principle if the location is specified on purpose, although
it's hard to be generated by mkfs due to the allocation policy
and rarely used by Android use case now mainly for > 4GiB files.

For now, only two fields `i_ctime_nsec` and `i_nlink' couldn't
be read from disk properly and cause out-of-bound memory read
with random value.

Let's fix now.

Fixes: 431339ba9042 ("staging: erofs: add inode operations")
Cc: <stable@vger.kernel.org> # 4.19+
Link: https://lore.kernel.org/r/20200729175801.GA23973@xiangao.remote.csb
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
[ Gao Xiang: resolve non-trivial conflicts for latest 4.19.y. ]
Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/staging/erofs/inode.c |  135 ++++++++++++++++++++++++++++--------------
 1 file changed, 90 insertions(+), 45 deletions(-)

--- a/drivers/staging/erofs/inode.c
+++ b/drivers/staging/erofs/inode.c
@@ -14,26 +14,78 @@
 
 #include <trace/events/erofs.h>
 
-/* no locking */
-static int read_inode(struct inode *inode, void *data)
+/*
+ * if inode is successfully read, return its inode page (or sometimes
+ * the inode payload page if it's an extended inode) in order to fill
+ * inline data if possible.
+ */
+static struct page *read_inode(struct inode *inode, unsigned int *ofs)
 {
+	struct super_block *sb = inode->i_sb;
+	struct erofs_sb_info *sbi = EROFS_SB(sb);
 	struct erofs_vnode *vi = EROFS_V(inode);
-	struct erofs_inode_v1 *v1 = data;
-	const unsigned advise = le16_to_cpu(v1->i_advise);
+	const erofs_off_t inode_loc = iloc(sbi, vi->nid);
+	erofs_blk_t blkaddr;
+	struct page *page;
+	struct erofs_inode_v1 *v1;
+	struct erofs_inode_v2 *v2, *copied = NULL;
+	unsigned int ifmt;
+	int err;
+
+	blkaddr = erofs_blknr(inode_loc);
+	*ofs = erofs_blkoff(inode_loc);
+
+	debugln("%s, reading inode nid %llu at %u of blkaddr %u",
+		__func__, vi->nid, *ofs, blkaddr);
 
-	vi->data_mapping_mode = __inode_data_mapping(advise);
+	page = erofs_get_meta_page(sb, blkaddr, false);
+	if (IS_ERR(page)) {
+		errln("failed to get inode (nid: %llu) page, err %ld",
+		      vi->nid, PTR_ERR(page));
+		return page;
+	}
+
+	v1 = page_address(page) + *ofs;
+	ifmt = le16_to_cpu(v1->i_advise);
 
+	vi->data_mapping_mode = __inode_data_mapping(ifmt);
 	if (unlikely(vi->data_mapping_mode >= EROFS_INODE_LAYOUT_MAX)) {
 		errln("unknown data mapping mode %u of nid %llu",
 			vi->data_mapping_mode, vi->nid);
-		DBG_BUGON(1);
-		return -EIO;
+		err = -EOPNOTSUPP;
+		goto err_out;
 	}
 
-	if (__inode_version(advise) == EROFS_INODE_LAYOUT_V2) {
-		struct erofs_inode_v2 *v2 = data;
-
+	switch (__inode_version(ifmt)) {
+	case EROFS_INODE_LAYOUT_V2:
 		vi->inode_isize = sizeof(struct erofs_inode_v2);
+		/* check if the inode acrosses page boundary */
+		if (*ofs + vi->inode_isize <= PAGE_SIZE) {
+			*ofs += vi->inode_isize;
+			v2 = (struct erofs_inode_v2 *)v1;
+		} else {
+			const unsigned int gotten = PAGE_SIZE - *ofs;
+
+			copied = kmalloc(vi->inode_isize, GFP_NOFS);
+			if (!copied) {
+				err = -ENOMEM;
+				goto err_out;
+			}
+			memcpy(copied, v1, gotten);
+			unlock_page(page);
+			put_page(page);
+
+			page = erofs_get_meta_page(sb, blkaddr + 1, false);
+			if (IS_ERR(page)) {
+				errln("failed to get inode payload page (nid: %llu), err %ld",
+				      vi->nid, PTR_ERR(page));
+				kfree(copied);
+				return page;
+			}
+			*ofs = vi->inode_isize - gotten;
+			memcpy((u8 *)copied + gotten, page_address(page), *ofs);
+			v2 = copied;
+		}
 		vi->xattr_isize = ondisk_xattr_ibody_size(v2->i_xattr_icount);
 
 		inode->i_mode = le16_to_cpu(v2->i_mode);
@@ -46,7 +98,7 @@ static int read_inode(struct inode *inod
 		} else if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
 			inode->i_rdev = 0;
 		} else {
-			return -EIO;
+			goto bogusimode;
 		}
 
 		i_uid_write(inode, le32_to_cpu(v2->i_uid));
@@ -58,10 +110,11 @@ static int read_inode(struct inode *inod
 		inode->i_ctime.tv_nsec = le32_to_cpu(v2->i_ctime_nsec);
 
 		inode->i_size = le64_to_cpu(v2->i_size);
-	} else if (__inode_version(advise) == EROFS_INODE_LAYOUT_V1) {
-		struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
-
+		kfree(copied);
+		break;
+	case EROFS_INODE_LAYOUT_V1:
 		vi->inode_isize = sizeof(struct erofs_inode_v1);
+		*ofs += vi->inode_isize;
 		vi->xattr_isize = ondisk_xattr_ibody_size(v1->i_xattr_icount);
 
 		inode->i_mode = le16_to_cpu(v1->i_mode);
@@ -74,7 +127,7 @@ static int read_inode(struct inode *inod
 		} else if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
 			inode->i_rdev = 0;
 		} else {
-			return -EIO;
+			goto bogusimode;
 		}
 
 		i_uid_write(inode, le16_to_cpu(v1->i_uid));
@@ -86,11 +139,12 @@ static int read_inode(struct inode *inod
 		inode->i_ctime.tv_nsec = sbi->build_time_nsec;
 
 		inode->i_size = le32_to_cpu(v1->i_size);
-	} else {
+		break;
+	default:
 		errln("unsupported on-disk inode version %u of nid %llu",
-			__inode_version(advise), vi->nid);
-		DBG_BUGON(1);
-		return -EIO;
+		      __inode_version(ifmt), vi->nid);
+		err = -EOPNOTSUPP;
+		goto err_out;
 	}
 
 	inode->i_mtime.tv_sec = inode->i_ctime.tv_sec;
@@ -100,7 +154,16 @@ static int read_inode(struct inode *inod
 
 	/* measure inode.i_blocks as the generic filesystem */
 	inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
-	return 0;
+	return page;
+bogusimode:
+	errln("bogus i_mode (%o) @ nid %llu", inode->i_mode, vi->nid);
+	err = -EIO;
+err_out:
+	DBG_BUGON(1);
+	kfree(copied);
+	unlock_page(page);
+	put_page(page);
+	return ERR_PTR(err);
 }
 
 /*
@@ -132,7 +195,7 @@ static int fill_inline_data(struct inode
 		if (unlikely(lnk == NULL))
 			return -ENOMEM;
 
-		m_pofs += vi->inode_isize + vi->xattr_isize;
+		m_pofs += vi->xattr_isize;
 
 		/* inline symlink data shouldn't across page boundary as well */
 		if (unlikely(m_pofs + inode->i_size > PAGE_SIZE)) {
@@ -153,35 +216,17 @@ static int fill_inline_data(struct inode
 
 static int fill_inode(struct inode *inode, int isdir)
 {
-	struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
-	struct erofs_vnode *vi = EROFS_V(inode);
 	struct page *page;
-	void *data;
-	int err;
-	erofs_blk_t blkaddr;
-	unsigned ofs;
+	unsigned int ofs;
+	int err = 0;
 
 	trace_erofs_fill_inode(inode, isdir);
 
-	blkaddr = erofs_blknr(iloc(sbi, vi->nid));
-	ofs = erofs_blkoff(iloc(sbi, vi->nid));
-
-	debugln("%s, reading inode nid %llu at %u of blkaddr %u",
-		__func__, vi->nid, ofs, blkaddr);
-
-	page = erofs_get_meta_page(inode->i_sb, blkaddr, isdir);
-
+	/* read inode base data from disk */
+	page = read_inode(inode, &ofs);
 	if (IS_ERR(page)) {
-		errln("failed to get inode (nid: %llu) page, err %ld",
-			vi->nid, PTR_ERR(page));
 		return PTR_ERR(page);
-	}
-
-	DBG_BUGON(!PageUptodate(page));
-	data = page_address(page);
-
-	err = read_inode(inode, data + ofs);
-	if (!err) {
+	} else {
 		/* setup the new inode */
 		if (S_ISREG(inode->i_mode)) {
 #ifdef CONFIG_EROFS_FS_XATTR
@@ -229,7 +274,7 @@ static int fill_inode(struct inode *inod
 		inode->i_mapping->a_ops = &erofs_raw_access_aops;
 
 		/* fill last page if inline data is available */
-		fill_inline_data(inode, data, ofs);
+		fill_inline_data(inode, page_address(page), ofs);
 	}
 
 out_unlock:



  reply	other threads:[~2021-05-05 12:05 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-05 12:05 [PATCH 4.19 00/15] 4.19.190-rc1 review Greg Kroah-Hartman
2021-05-05 12:05 ` Greg Kroah-Hartman [this message]
2021-05-05 12:05 ` [PATCH 4.19 02/15] ACPI: tables: x86: Reserve memory occupied by ACPI tables Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 03/15] ACPI: x86: Call acpi_boot_table_init() after acpi_table_upgrade() Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 04/15] net: usb: ax88179_178a: initialize local variables before use Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 05/15] iwlwifi: Fix softirq/hardirq disabling in iwl_pcie_enqueue_hcmd() Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 06/15] mips: Do not include hi and lo in clobber list for R6 Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 07/15] bpf: Fix masking negation logic upon negative dst register Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 08/15] iwlwifi: Fix softirq/hardirq disabling in iwl_pcie_gen2_enqueue_hcmd() Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 09/15] ALSA: usb-audio: Add MIDI quirk for Vox ToneLab EX Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 10/15] USB: Add LPM quirk for Lenovo ThinkPad USB-C Dock Gen2 Ethernet Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 11/15] USB: Add reset-resume quirk for WD19s Realtek Hub Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 12/15] ASoC: ak4458: Add MODULE_DEVICE_TABLE Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 13/15] ASoC: ak5558: " Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 14/15] platform/x86: thinkpad_acpi: Correct thermal sensor allocation Greg Kroah-Hartman
2021-05-05 12:05 ` [PATCH 4.19 15/15] ovl: allow upperdir inside lowerdir Greg Kroah-Hartman
2021-05-05 21:06 ` [PATCH 4.19 00/15] 4.19.190-rc1 review Shuah Khan
2021-05-06  1:46 ` Guenter Roeck
2021-05-06  1:47 ` Guenter Roeck
2021-05-06  2:02   ` Guenter Roeck
2021-05-06  9:39 ` Naresh Kamboju
2021-05-06 10:02 ` Sudip Mukherjee
2021-05-06 21:46 ` Pavel Machek
2021-05-07  1:06 ` Samuel Zou

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=20210505120503.831517445@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=hsiangkao@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=yuchao0@huawei.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).