From nobody Sun May 24 22:35:57 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69774246BC0 for ; Thu, 21 May 2026 02:15:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779329717; cv=none; b=PQAZNeCqz7nX8usHJ7cUd+IdBhcIr9q34ilEFThQPU5KQYceZeTsMQW6wCYntSp1tk1CNEorTZoLzFx6exc9bKqBD+bChG5kAs1nuWWBeoTjjYdWE2X9NYXLPuZxvt1rJOLYk1XFAP5MR5hncQRoPHZ7A/2dYNMV53PHRQTf0AA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779329717; c=relaxed/simple; bh=Z5+H0kZRPe3AI+FA6oJ4got58IG2K4qBKodZ1E5O5Zc=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=sxpCFsBDIn97+ZDiIsm/Rfp/V22SELWNyJX978Bv3QgT00fqsFcGP8++yREgpi9qaxsggpAJqkoW8DYH4uDKsuDSJKlkZPMiLkZJgrZ0frW9C0UUGHEp6VbGIDLyeu1hbeGI3Z5fcY3EDTaC8SOD/r53izIIGfIRrcJN6X9X0Z0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Pi+EJ8Uc; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Pi+EJ8Uc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 95E251F000E9; Thu, 21 May 2026 02:15:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1779329716; bh=HZcIQLx9iPSfq0a9llYdjKnoSgctdWl+r0Xda9CXSWY=; h=From:To:Cc:Subject:Date; b=Pi+EJ8Uc6o7cC3saPaTyIRV95wcKxyvTergMusxBtmHj0EaJsxcxRdd/h46P/S16m fXcTUu85uvu0w21NkwOqkcy2Zd8zbg88SbDEvQqkCb6IIbLiPQrUW3qXof5ozTtvSo K2+gW2opo7c7b/c0bJOMxXLrC0iM0WahWMv/kixkDZyoEMgMZ2+ZAWlo5UE+LmHqcA pa8BfOVrwV95J+kGU0N6E/6IlnTyKDe4dzkJ0h4b3cU53Ij5vvWPpsRAtYRqycsCbQ C9tzb4Z7OaZvLfuEtjAyNsqsVaS1YsnBPR8oY5SDfS93oJoYdR82bWiAUvzaQGeZLB iP+0jzma6sh4w== From: Chao Yu To: jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, Chao Yu , stable@kernel.org, Daeho Jeong , Sunmin Jeong Subject: [PATCH] f2fs: atomic: fix UAF issue on f2fs_inode_info.atomic_inode Date: Thu, 21 May 2026 10:15:05 +0800 Message-ID: <20260521021505.1357466-1-chao@kernel.org> X-Mailer: git-send-email 2.54.0.669.g59709faab0-goog Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" - ioctl(F2FS_IOC_GARBAGE_COLLECT_RANGE) - shrink - f2fs_gc - gc_data_segment - ra_data_block(cow_inode) - mapping =3D F2FS_I(inode)->atomic_inode->i_mapping : f2fs_is_cow_file(cow_inode) is true - f2fs_evict_inode(atomic_inode) - clear_inode_flag(fi->cow_inode, FI_COW_FILE) - F2FS_I(fi->cow_inode)->atomic_inode =3D NULL ... - truncate_inode_pages_final(atomic_inode) - f2fs_grab_cache_folio(mapping) : create folio in atomic_inode->mapping - clear_inode(atomic_inode) - BUG_ON(atomic_inode->i_data.nrpages) We need to add a reference on fi->atomic_inode before using its mapping field during garbage collection, otherwise, it will cause UAF issue. Cc: stable@kernel.org Cc: Daeho Jeong Cc: Sunmin Jeong Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Fixes: f18d00769336 ("f2fs: use meta inode for GC of COW file") Signed-off-by: Chao Yu --- fs/f2fs/gc.c | 50 +++++++++++++++++++++++++++++++++++++++++-------- fs/f2fs/inode.c | 11 ++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 3048cc47b5e0..0e537508df20 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct= f2fs_summary *sum, static int ra_data_block(struct inode *inode, pgoff_t index) { struct f2fs_sb_info *sbi =3D F2FS_I_SB(inode); - struct address_space *mapping =3D f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping =3D inode->i_mapping; + struct inode *atomic_inode =3D NULL; struct dnode_of_data dn; struct folio *folio, *efolio; struct f2fs_io_info fio =3D { @@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_= t index) }; int err =3D 0; =20 + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode =3D igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping =3D atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + folio =3D f2fs_grab_cache_folio(mapping, index, true); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err =3D PTR_ERR(folio); + goto out_iput; + } =20 if (f2fs_lookup_read_extent_cache_block(inode, index, &dn.data_blkaddr)) { @@ -1299,11 +1312,16 @@ static int ra_data_block(struct inode *inode, pgoff= _t index) f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE); f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE); =20 + if (atomic_inode) + iput(atomic_inode); return 0; put_encrypted_page: f2fs_put_page(fio.encrypted_page, true); put_folio: f2fs_folio_put(folio, true); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } =20 @@ -1314,8 +1332,8 @@ static int ra_data_block(struct inode *inode, pgoff_t= index) static int move_data_block(struct inode *inode, block_t bidx, int gc_type, unsigned int segno, int off) { - struct address_space *mapping =3D f2fs_is_cow_file(inode) ? - F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; + struct address_space *mapping =3D inode->i_mapping; + struct inode *atomic_inode =3D NULL; struct f2fs_io_info fio =3D { .sbi =3D F2FS_I_SB(inode), .ino =3D inode->i_ino, @@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, blo= ck_t bidx, (fio.sbi->gc_mode !=3D GC_URGENT_HIGH) ? CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; =20 + f2fs_down_read(&F2FS_I(inode)->i_sem); + if (f2fs_is_cow_file(inode)) { + atomic_inode =3D igrab(F2FS_I(inode)->atomic_inode); + if (!atomic_inode) { + f2fs_up_read(&F2FS_I(inode)->i_sem); + return -EBUSY; + } + mapping =3D atomic_inode->i_mapping; + } + f2fs_up_read(&F2FS_I(inode)->i_sem); + /* do not read out */ folio =3D f2fs_grab_cache_folio(mapping, bidx, false); - if (IS_ERR(folio)) - return PTR_ERR(folio); + if (IS_ERR(folio)) { + err =3D PTR_ERR(folio); + goto out_iput; + } =20 if (!check_valid_map(F2FS_I_SB(inode), segno, off)) { err =3D -ENOENT; @@ -1473,6 +1504,9 @@ static int move_data_block(struct inode *inode, block= _t bidx, folio_unlock(folio); folio_end_dropbehind(folio); folio_put(folio); +out_iput: + if (atomic_inode) + iput(atomic_inode); return err; } =20 diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 12f982f87f15..315682e5da53 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -875,10 +875,15 @@ void f2fs_evict_inode(struct inode *inode) f2fs_abort_atomic_write(inode, true); =20 if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { - clear_inode_flag(fi->cow_inode, FI_COW_FILE); - F2FS_I(fi->cow_inode)->atomic_inode =3D NULL; - iput(fi->cow_inode); + struct inode *cow_inode =3D fi->cow_inode; + + f2fs_down_write(&F2FS_I(cow_inode)->i_sem); + clear_inode_flag(cow_inode, FI_COW_FILE); + F2FS_I(cow_inode)->atomic_inode =3D NULL; fi->cow_inode =3D NULL; + f2fs_up_write(&F2FS_I(cow_inode)->i_sem); + + iput(cow_inode); } =20 trace_f2fs_evict_inode(inode); --=20 2.49.0