Received: by 2002:a05:6a10:a852:0:0:0:0 with SMTP id d18csp493809pxy; Wed, 5 May 2021 07:11:55 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwE0XguGa9s5QgXgCkgtfjK/wUXYJWbWQzg1omOjTwSWxWIOcp8Jf7AGFsiL+y1MruPZ1KX X-Received: by 2002:a17:907:8323:: with SMTP id mq35mr27879506ejc.391.1620223915106; Wed, 05 May 2021 07:11:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1620223915; cv=none; d=google.com; s=arc-20160816; b=B6H4NB+gEvw8p0fWhoVA0fdr6Y6S05Av/qNj1ecuX4NZW9YMhFI8UD3NSxCURu8lyG tmlH28vT9/w2BtVCs4abZDDACj55d6B/C72cGe2zvGE06j16h4woeNmlEwRgIgAXBtqN 0HuCXCzH5P4rvPsWSEX94v17zIia9msV4I1jvc2pRjoFs0k2v+ui4qgrKVdrEAsZnZE0 wZFW2rSu9nxXL73ygUMysf3PPXGdtrtKDFhNwTmtCWzGFc70rkFZ1d8qXfvj/CvqKEw+ DYjLsDDfKFNOWEM6G0N6y0TUcWUeyH6utG+1d08+Vkg3ws3XzfRgyZmAZTAqQmfK/1YI 3NOQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=VULBm+X9OX8nbI7fLHCXecK+j/G+GwIf2CJ6s1IuoK4=; b=T6yguEx2CUHLAs0Ed6233NS4yF5Qii49N89ApyjPZmlaTwO/wvEZzGpNglGKjl9DBt uDlJS98CRMkKsmb4OXJNHgrvpyc8Q4fWI2QLq8L3ASbOhTd79uCuCgAHjNvzvg2TBn/E /bGFyg/m4zlorE6AYDOgtwkyI4OkJ5tK0MMzAx2blyfIm/jrHG7Q+YmfboGRTOuWZMbR TUWrvWREZs5qa9z0dqRhzKV2rI16rhaPvMFvEZFA2Zik/u7Ja/XenISo+qbdAgovOGf2 cKFDQ9YPSUTJJvLX8s+9Gx+SXZ5xO5tf96crNGzW+Un8qyrEbxQlvRfHK5WgyU4OTBmi TdYw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=1lW6Ll+7; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id kl2si6701226ejc.82.2021.05.05.07.11.30; Wed, 05 May 2021 07:11:55 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=1lW6Ll+7; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233284AbhEEMGt (ORCPT + 99 others); Wed, 5 May 2021 08:06:49 -0400 Received: from mail.kernel.org ([198.145.29.99]:44986 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233301AbhEEMGa (ORCPT ); Wed, 5 May 2021 08:06:30 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7CB8A611EE; Wed, 5 May 2021 12:05:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1620216334; bh=LKM4pejhjYdlDuoJ8F2CXkLUbs+59+cO6jSISukbYgM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=1lW6Ll+79s1W1J51R0J/BiTxu0ErskK4miXui5rP+QzQGoEq/B5ae4vP980u/Pzz9 ijITyR9G5u1smMHZAbHFNA59mTlRlthQ1PhmvrkvP/9FqfXG7gs+nMoIbw162UZVz0 Gj+EBLPeHNUpXmiDGdvCiV5gIyFNdc6clP6Y/U6U= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Chao Yu , Gao Xiang Subject: [PATCH 4.19 01/15] erofs: fix extended inode could cross boundary Date: Wed, 5 May 2021 14:05:06 +0200 Message-Id: <20210505120503.831517445@linuxfoundation.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210505120503.781531508@linuxfoundation.org> References: <20210505120503.781531508@linuxfoundation.org> User-Agent: quilt/0.66 X-stable: review X-Patchwork-Hint: ignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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: