From: Greg Kroah-Hartman Date: Fri, 30 Apr 2021 13:50:31 +0000 (+0200) Subject: 4.19-stable patches X-Git-Tag: v5.4.116~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1de1a5ed33da317892660762149b0df72d6d8042;p=thirdparty%2Fkernel%2Fstable-queue.git 4.19-stable patches added patches: erofs-fix-extended-inode-could-cross-boundary.patch --- diff --git a/queue-4.19/erofs-fix-extended-inode-could-cross-boundary.patch b/queue-4.19/erofs-fix-extended-inode-could-cross-boundary.patch new file mode 100644 index 00000000000..572e3cfedfe --- /dev/null +++ b/queue-4.19/erofs-fix-extended-inode-could-cross-boundary.patch @@ -0,0 +1,253 @@ +From 0dcd3c94e02438f4a571690e26f4ee997524102a Mon Sep 17 00:00:00 2001 +From: Gao Xiang +Date: Thu, 30 Jul 2020 01:58:01 +0800 +Subject: erofs: fix extended inode could cross boundary + +From: Gao Xiang + +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: # 4.19+ +Link: https://lore.kernel.org/r/20200729175801.GA23973@xiangao.remote.csb +Reviewed-by: Chao Yu +Signed-off-by: Gao Xiang +[ Gao Xiang: resolve non-trivial conflicts for latest 4.19.y. ] +Signed-off-by: Gao Xiang +Signed-off-by: Greg Kroah-Hartman +--- + 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 + +-/* 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: diff --git a/queue-4.19/series b/queue-4.19/series new file mode 100644 index 00000000000..155c39b8d28 --- /dev/null +++ b/queue-4.19/series @@ -0,0 +1 @@ +erofs-fix-extended-inode-could-cross-boundary.patch