exfat: advance valid_size to EOF for append writes

For an append write, the write position is recalculated by
generic_write_checks(). Therefore, the write position must be obtained
after generic_write_checks() returns.

Using the original write position to advance valid_size may cause the
valid_size update to be skipped, potentially exposing stale data.

Also move the truncate_pagecache() check below the recalculated
position, so that pos is never read before it is assigned.

Fixes: 11a347fb6c ("exfat: change to get file size from DataLength")
Cc: stable@vger.kernel.org
Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Chi Zhiling 2026-10-04 18:19:30 +08:00 committed by Namjae Jeon
parent 1306c1e6b0
commit b70ab40750

View File

@ -854,8 +854,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct exfat_inode_info *ei = EXFAT_I(inode);
loff_t pos = iocb->ki_pos;
loff_t valid_size;
loff_t pos, valid_size;
int err;
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
@ -863,11 +862,6 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
inode_lock(inode);
if (pos > i_size_read(inode))
truncate_pagecache(inode, i_size_read(inode));
valid_size = ei->valid_size;
ret = generic_write_checks(iocb, iter);
if (ret <= 0)
goto unlock;
@ -878,6 +872,11 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
goto unlock;
}
pos = iocb->ki_pos;
if (pos > i_size_read(inode))
truncate_pagecache(inode, i_size_read(inode));
valid_size = ei->valid_size;
if (pos > valid_size) {
ret = exfat_extend_valid_size(inode, pos);
if (ret < 0 && ret != -ENOSPC) {
@ -887,6 +886,8 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
}
if (ret < 0)
goto unlock;
pos = valid_size;
}
if (iocb->ki_flags & IOCB_DIRECT)
@ -899,9 +900,6 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
inode_unlock(inode);
if (pos > valid_size)
pos = valid_size;
if (iocb->ki_pos > pos) {
ssize_t err = generic_write_sync(iocb, iocb->ki_pos - pos);