VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46175

CVE-2026-46175

Description

In the Linux kernel, the following vulnerability has been resolved:

f2fs: fix fsck inconsistency caused by FGGC of node block

During FGGC node block migration, fsck may incorrectly treat the migrated node block as fsync-written data.

The reproduction scenario: root@vm:/mnt/f2fs# seq 1 2048 | xargs -n 1 ./test_sync // write inline inode and sync root@vm:/mnt/f2fs# rm -f 1 root@vm:/mnt/f2fs# sync root@vm:/mnt/f2fs# f2fs_io gc_range // move data block in sync mode and not write CP SPO, "fsck --dry-run" find inode has already checkpointed but still with DENT_BIT_SHIFT set

The root cause is that GC does not clear the dentry mark and fsync mark during node block migration, leading fsck to misinterpret them as user-issued fsync writes.

In BGGC mode, node block migration is handled by f2fs_sync_node_pages(), which guarantees the dentry and fsync marks are cleared before writing.

This patch move the set/clear of the fsync|dentry marks into __write_node_folio to make the logic clearer, and ensures the fsync|dentry mark is cleared in FGGC.

Affected products

2

Patches

6
8be551f538dc

f2fs: fix fsck inconsistency caused by FGGC of node block

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitYongpeng YangMar 18, 2026Fixed in 6.18.30via kernel-cna
2 files changed · +26 30
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index e136c8b16d29e0..ca3dad7418b268 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1709,9 +1709,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1783,6 +1784,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1849,7 +1852,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1896,6 +1899,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1926,11 +1930,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1947,8 +1948,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2148,10 +2150,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index e136c8b16d29e0..ca3dad7418b268 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1709,9 +1709,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1783,6 +1784,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1849,7 +1852,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1896,6 +1899,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1926,11 +1930,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1947,8 +1948,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2148,10 +2150,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
e7c6d30169b0

f2fs: fix fsck inconsistency caused by FGGC of node block

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitYongpeng YangMar 18, 2026Fixed in 7.0.7via kernel-cna
2 files changed · +26 30
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index ebab504f22c8c9..9ff954952a151d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1729,9 +1729,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1804,6 +1805,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1870,7 +1873,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1917,6 +1920,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1947,11 +1951,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1968,8 +1969,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2169,10 +2171,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index ebab504f22c8c9..9ff954952a151d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1729,9 +1729,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1804,6 +1805,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1870,7 +1873,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1917,6 +1920,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1947,11 +1951,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1968,8 +1969,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2169,10 +2171,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
c3e238bd1f56

f2fs: fix fsck inconsistency caused by FGGC of node block

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitYongpeng YangMar 18, 2026Fixed in 7.1-rc1via kernel-cna
2 files changed · +26 30
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index 630fd3b43a089e..c7499cb527453d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1727,9 +1727,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1802,6 +1803,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1868,7 +1871,7 @@ static int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1915,6 +1918,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1945,11 +1949,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1966,8 +1967,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2167,10 +2169,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index 630fd3b43a089e..c7499cb527453d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1727,9 +1727,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1802,6 +1803,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1868,7 +1871,7 @@ static int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1915,6 +1918,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1945,11 +1949,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1966,8 +1967,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2167,10 +2169,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
8be551f538dc

f2fs: fix fsck inconsistency caused by FGGC of node block

2 files changed · +26 30
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index e136c8b16d29e0..ca3dad7418b268 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1709,9 +1709,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1783,6 +1784,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1849,7 +1852,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1896,6 +1899,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1926,11 +1930,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1947,8 +1948,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2148,10 +2150,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index e136c8b16d29e0..ca3dad7418b268 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1709,9 +1709,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1783,6 +1784,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1849,7 +1852,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1896,6 +1899,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1926,11 +1930,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1947,8 +1948,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2148,10 +2150,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
c3e238bd1f56

f2fs: fix fsck inconsistency caused by FGGC of node block

2 files changed · +26 30
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index 630fd3b43a089e..c7499cb527453d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1727,9 +1727,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1802,6 +1803,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1868,7 +1871,7 @@ static int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1915,6 +1918,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1945,11 +1949,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1966,8 +1967,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2167,10 +2169,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index 630fd3b43a089e..c7499cb527453d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1727,9 +1727,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1802,6 +1803,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1868,7 +1871,7 @@ static int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1915,6 +1918,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1945,11 +1949,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1966,8 +1967,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2167,10 +2169,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
e7c6d30169b0

f2fs: fix fsck inconsistency caused by FGGC of node block

2 files changed · +26 30
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index ebab504f22c8c9..9ff954952a151d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1729,9 +1729,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1804,6 +1805,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1870,7 +1873,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1917,6 +1920,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1947,11 +1951,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1968,8 +1969,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2169,10 +2171,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    
  • fs/f2fs/node.c+13 15 modified
    diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
    index ebab504f22c8c9..9ff954952a151d 100644
    --- a/fs/f2fs/node.c
    +++ b/fs/f2fs/node.c
    @@ -1729,9 +1729,10 @@ continue_unlock:
     	return last_folio;
     }
     
    -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted,
    -				struct writeback_control *wbc, bool do_balance,
    -				enum iostat_type io_type, unsigned int *seq_id)
    +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
    +				bool *submitted, struct writeback_control *wbc,
    +				bool do_balance, enum iostat_type io_type,
    +				unsigned int *seq_id)
     {
     	struct f2fs_sb_info *sbi = F2FS_F_SB(folio);
     	nid_t nid;
    @@ -1804,6 +1805,8 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted
     	if (atomic && !test_opt(sbi, NOBARRIER))
     		fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
     
    +	set_dentry_mark(folio, false);
    +	set_fsync_mark(folio, do_fsync);
     	if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio)))
     		set_dentry_mark(folio,
     				f2fs_need_dentry_mark(sbi, ino_of_node(folio)));
    @@ -1870,7 +1873,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
     		goto out_folio;
     	}
     
    -	if (!__write_node_folio(node_folio, false, NULL,
    +	if (!__write_node_folio(node_folio, false, false, NULL,
     				&wbc, false, FS_GC_NODE_IO, NULL))
     		err = -EAGAIN;
     	goto release_folio;
    @@ -1917,6 +1920,7 @@ retry:
     		for (i = 0; i < nr_folios; i++) {
     			struct folio *folio = fbatch.folios[i];
     			bool submitted = false;
    +			bool do_fsync = false;
     
     			if (unlikely(f2fs_cp_error(sbi))) {
     				f2fs_folio_put(last_folio, false);
    @@ -1947,11 +1951,8 @@ continue_unlock:
     
     			f2fs_folio_wait_writeback(folio, NODE, true, true);
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
     			if (!atomic || folio == last_folio) {
    -				set_fsync_mark(folio, 1);
    +				do_fsync = true;
     				percpu_counter_inc(&sbi->rf_node_block_count);
     				if (IS_INODE(folio)) {
     					if (is_inode_flag_set(inode,
    @@ -1968,8 +1969,9 @@ continue_unlock:
     
     			if (!__write_node_folio(folio, atomic &&
     						folio == last_folio,
    -						&submitted, wbc, true,
    -						FS_NODE_IO, seq_id)) {
    +						do_fsync, &submitted,
    +						wbc, true, FS_NODE_IO,
    +						seq_id)) {
     				f2fs_folio_put(last_folio, false);
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    @@ -2169,10 +2171,7 @@ write_node:
     			if (!folio_clear_dirty_for_io(folio))
     				goto continue_unlock;
     
    -			set_fsync_mark(folio, 0);
    -			set_dentry_mark(folio, 0);
    -
    -			if (!__write_node_folio(folio, false, &submitted,
    +			if (!__write_node_folio(folio, false, false, &submitted,
     					wbc, do_balance, io_type, NULL)) {
     				folio_batch_release(&fbatch);
     				ret = -EIO;
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Foreground GC does not clear the dentry mark and fsync mark during node block migration, causing fsck to misinterpret migrated node blocks as user-issued fsync writes."

Attack vector

An attacker with local filesystem access can trigger a scenario where inline inodes are written and synced, then deleted, followed by a foreground GC (`f2fs_io gc_range`) that migrates node blocks without writing a checkpoint. After a sudden power-off (SPO), `fsck --dry-run` finds that the inode has already been checkpointed but still has `DENT_BIT_SHIFT` set, causing fsck inconsistency [patch_id=2898039]. The root cause is that FGGC does not clear the dentry mark and fsync mark during node block migration, so fsck misinterprets the migrated node block as user-issued fsync-written data.

Affected code

The vulnerability is in `fs/f2fs/node.c` in the `__write_node_folio()` function and its callers. The FGGC (Foreground Garbage Collection) path calls `f2fs_write_single_node_folio()` which invokes `__write_node_folio()` without clearing the fsync and dentry marks on the node folio before writing [patch_id=2898039].

What the fix does

The patch moves the `set_fsync_mark()` and `set_dentry_mark()` calls into `__write_node_folio()` itself, adding a new `do_fsync` parameter. Previously, callers like `f2fs_write_single_node_folio()` (used by FGGC) did not clear these marks before writing, while `f2fs_sync_node_pages()` (used by BGGC) did so manually. By centralizing the mark management inside `__write_node_folio()`, the fix ensures that every node folio write — including FGGC writes — always clears the dentry mark and sets the fsync mark only when `do_fsync` is true [patch_id=2898039].

Preconditions

  • inputAttacker must be able to create and delete files on an f2fs filesystem, then trigger foreground garbage collection (e.g. via f2fs_io gc_range)
  • configA sudden power-off (SPO) must occur after the GC migration but before a checkpoint write

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.