From 80ff677b55b5186b2844a2d4f4c91c01ef836276 Mon Sep 17 00:00:00 2001 From: Ethan Ferguson Date: Thu, 28 Aug 2025 16:37:14 -0400 Subject: [PATCH 1/8] ntfs3: transition magic number to shared constant Use the common FSLABEL_MAX constant instead of a hardcoded magic constant of 256. Signed-off-by: Ethan Ferguson Signed-off-by: Konstantin Komarov --- fs/ntfs3/ntfs_fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 1296e6fcc779..630128716ea7 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -280,7 +280,7 @@ struct ntfs_sb_info { __le16 flags; // Cached current VOLUME_INFO::flags, VOLUME_FLAG_DIRTY. u8 major_ver; u8 minor_ver; - char label[256]; + char label[FSLABEL_MAX]; bool real_dirty; // Real fs state. } volume; From e4dff970091118f2ecf66146d0b286979078b5cc Mon Sep 17 00:00:00 2001 From: Ethan Ferguson Date: Thu, 28 Aug 2025 16:37:15 -0400 Subject: [PATCH 2/8] ntfs3: add FS_IOC_GETFSLABEL ioctl Add support for the FS_IOC_GETFSLABEL ioctl. Signed-off-by: Ethan Ferguson Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index c1ece707b195..0a1e9f16ffaf 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -49,6 +49,14 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg) return 0; } +static int ntfs_ioctl_get_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf) +{ + if (copy_to_user(buf, sbi->volume.label, FSLABEL_MAX)) + return -EFAULT; + + return 0; +} + /* * ntfs_ioctl - file_operations::unlocked_ioctl */ @@ -64,6 +72,8 @@ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) switch (cmd) { case FITRIM: return ntfs_ioctl_fitrim(sbi, arg); + case FS_IOC_GETFSLABEL: + return ntfs_ioctl_get_volume_label(sbi, (u8 __user *)arg); } return -ENOTTY; /* Inappropriate ioctl for device. */ } From 21dc07ac9c25f01ab2023e3e8605861d8fd6e00c Mon Sep 17 00:00:00 2001 From: Ethan Ferguson Date: Thu, 28 Aug 2025 16:37:16 -0400 Subject: [PATCH 3/8] ntfs3: add FS_IOC_SETFSLABEL ioctl Add support for the FS_IOC_SETFSLABEL ioctl. Signed-off-by: Ethan Ferguson Signed-off-by: Konstantin Komarov --- fs/ntfs3/file.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 0a1e9f16ffaf..4c90ec2fa2ea 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -57,6 +57,22 @@ static int ntfs_ioctl_get_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf) return 0; } +static int ntfs_ioctl_set_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf) +{ + u8 user[FSLABEL_MAX] = {0}; + int len; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(user, buf, FSLABEL_MAX)) + return -EFAULT; + + len = strnlen(user, FSLABEL_MAX); + + return ntfs_set_label(sbi, user, len); +} + /* * ntfs_ioctl - file_operations::unlocked_ioctl */ @@ -74,6 +90,8 @@ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) return ntfs_ioctl_fitrim(sbi, arg); case FS_IOC_GETFSLABEL: return ntfs_ioctl_get_volume_label(sbi, (u8 __user *)arg); + case FS_IOC_SETFSLABEL: + return ntfs_ioctl_set_volume_label(sbi, (u8 __user *)arg); } return -ENOTTY; /* Inappropriate ioctl for device. */ } From 4e8011ffec79717e5fdac43a7e79faf811a384b7 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 2 Sep 2025 19:43:24 +0900 Subject: [PATCH 4/8] ntfs3: pretend $Extend records as regular files Since commit af153bb63a33 ("vfs: catch invalid modes in may_open()") requires any inode be one of S_IFDIR/S_IFLNK/S_IFREG/S_IFCHR/S_IFBLK/ S_IFIFO/S_IFSOCK type, use S_IFREG for $Extend records. Reported-by: syzbot Closes: https://syzkaller.appspot.com/bug?extid=895c23f6917da440ed0d Signed-off-by: Tetsuo Handa Signed-off-by: Konstantin Komarov --- fs/ntfs3/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 37cbbee7fa58..b08b00912165 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -471,6 +471,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, fname->home.seq == cpu_to_le16(MFT_REC_EXTEND)) { /* Records in $Extend are not a files or general directories. */ inode->i_op = &ntfs_file_inode_operations; + mode = S_IFREG; } else { err = -EINVAL; goto out; From 736fc7bf5f68f6b74a0925b7e072c571838657d2 Mon Sep 17 00:00:00 2001 From: Vitaly Grigoryev Date: Mon, 25 Aug 2025 13:08:55 +0300 Subject: [PATCH 5/8] fs: ntfs3: Fix integer overflow in run_unpack() The MFT record relative to the file being opened contains its runlist, an array containing information about the file's location on the physical disk. Analysis of all Call Stack paths showed that the values of the runlist array, from which LCNs are calculated, are not validated before run_unpack function. The run_unpack function decodes the compressed runlist data format from MFT attributes (for example, $DATA), converting them into a runs_tree structure, which describes the mapping of virtual clusters (VCN) to logical clusters (LCN). The NTFS3 subsystem also has a shortcut for deleting files from MFT records - in this case, the RUN_DEALLOCATE command is sent to the run_unpack input, and the function logic provides that all data transferred to the runlist about file or directory is deleted without creating a runs_tree structure. Substituting the runlist in the $DATA attribute of the MFT record for an arbitrary file can lead either to access to arbitrary data on the disk bypassing access checks to them (since the inode access check occurs above) or to destruction of arbitrary data on the disk. Add overflow check for addition operation. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Signed-off-by: Vitaly Grigoryev Signed-off-by: Konstantin Komarov --- fs/ntfs3/run.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 6e86d66197ef..88550085f745 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "debug.h" #include "ntfs.h" @@ -982,14 +983,18 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, if (!dlcn) return -EINVAL; - lcn = prev_lcn + dlcn; + + if (check_add_overflow(prev_lcn, dlcn, &lcn)) + return -EINVAL; prev_lcn = lcn; } else { /* The size of 'dlcn' can't be > 8. */ return -EINVAL; } - next_vcn = vcn64 + len; + if (check_add_overflow(vcn64, len, &next_vcn)) + return -EINVAL; + /* Check boundary. */ if (next_vcn > evcn + 1) return -EINVAL; @@ -1153,7 +1158,8 @@ int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) return -EINVAL; run_buf += size_size + offset_size; - vcn64 += len; + if (check_add_overflow(vcn64, len, &vcn64)) + return -EINVAL; #ifndef CONFIG_NTFS3_64BIT_CLUSTER if (vcn64 > 0x100000000ull) From d68318471aa2e16222ebf492883e05a2d72b9b17 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 15 Jul 2025 17:51:20 +0800 Subject: [PATCH 6/8] fs/ntfs3: Fix a resource leak bug in wnd_extend() Add put_bh() to decrease the refcount of 'bh' after the job is finished, preventing a resource leak. Fixes: 3f3b442b5ad2 ("fs/ntfs3: Add bitmap") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Signed-off-by: Konstantin Komarov --- fs/ntfs3/bitmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs3/bitmap.c b/fs/ntfs3/bitmap.c index 04107b950717..65d05e6a0566 100644 --- a/fs/ntfs3/bitmap.c +++ b/fs/ntfs3/bitmap.c @@ -1371,6 +1371,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) mark_buffer_dirty(bh); unlock_buffer(bh); /* err = sync_dirty_buffer(bh); */ + put_bh(bh); b0 = 0; bits -= op; From 0dc7117da8f92dd5fe077d712a756eccbe377d40 Mon Sep 17 00:00:00 2001 From: Moon Hee Lee Date: Tue, 22 Jul 2025 10:40:16 -0700 Subject: [PATCH 7/8] fs/ntfs3: reject index allocation if $BITMAP is empty but blocks exist Index allocation requires at least one bit in the $BITMAP attribute to track usage of index entries. If the bitmap is empty while index blocks are already present, this reflects on-disk corruption. syzbot triggered this condition using a malformed NTFS image. During a rename() operation involving a long filename (which spans multiple index entries), the empty bitmap allowed the name to be added without valid tracking. Subsequent deletion of the original entry failed with -ENOENT, due to unexpected index state. Reject such cases by verifying that the bitmap is not empty when index blocks exist. Reported-by: syzbot+b0373017f711c06ada64@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b0373017f711c06ada64 Fixes: d99208b91933 ("fs/ntfs3: cancle set bad inode after removing name fails") Tested-by: syzbot+b0373017f711c06ada64@syzkaller.appspotmail.com Signed-off-by: Moon Hee Lee Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 1bf2a6593dec..6d1bf890929d 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1508,6 +1508,16 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, bmp_size = bmp_size_v = le32_to_cpu(bmp->res.data_size); } + /* + * Index blocks exist, but $BITMAP has zero valid bits. + * This implies an on-disk corruption and must be rejected. + */ + if (in->name == I30_NAME && + unlikely(bmp_size_v == 0 && indx->alloc_run.count)) { + err = -EINVAL; + goto out1; + } + bit = bmp_size << 3; } From 7d460636b6402343ca150682f7bae896c4ff2a76 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 Aug 2025 08:10:08 +0200 Subject: [PATCH 8/8] ntfs3: stop using write_cache_pages Stop using the obsolete write_cache_pages and use writeback_iter directly. Signed-off-by: Christoph Hellwig Signed-off-by: Konstantin Komarov --- fs/ntfs3/inode.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index b08b00912165..3959f23c487a 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -872,9 +872,9 @@ int ntfs_set_size(struct inode *inode, u64 new_size) } static int ntfs_resident_writepage(struct folio *folio, - struct writeback_control *wbc, void *data) + struct writeback_control *wbc) { - struct address_space *mapping = data; + struct address_space *mapping = folio->mapping; struct inode *inode = mapping->host; struct ntfs_inode *ni = ntfs_i(inode); int ret; @@ -908,9 +908,14 @@ static int ntfs_writepages(struct address_space *mapping, if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) return -EIO; - if (is_resident(ntfs_i(inode))) - return write_cache_pages(mapping, wbc, ntfs_resident_writepage, - mapping); + if (is_resident(ntfs_i(inode))) { + struct folio *folio = NULL; + int error; + + while ((folio = writeback_iter(mapping, wbc, folio, &error))) + error = ntfs_resident_writepage(folio, wbc); + return error; + } return mpage_writepages(mapping, wbc, ntfs_get_block); }