Skip to content

Commit 0c0b282

Browse files
committed
Merge tag 'ntfs-for-7.1-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs
Pull ntfs fixes from Namjae Jeon: - Check the index depth limit via ntfs_icx_parent_inc(), avoiding context corruption from excessively deep child chains - Switch security descriptor allocation to kzalloc() to avoid leaking uninitialized memory - Prevent an inconsistent state where vol->volume_label becomes NULL on allocation failure - Validate MFT records by verifying that attrs_offset sits within bytes_in_use - Fix an off-by-one boundary comparison, correctly catching the out-of-range MFT record number - Validate the attribute name offset and length bounds prior to AT_UNUSED enumeration - Check for a valid left neighbor before runlist merges to prevent an 8byte out-of-bounds write on crafted volumes - Add the missing record comparison against $MFTMirr during mount - Fix wrong inode lookup when writing extent MFT records - Redirty folio on memory allocation failure in ntfs_write_mft_block() - Capture and propagate $MFTMirr sync errors during writeback - Ensure MFT mirror and synchronous writes wait for I/O completion - Fix buffer overflow/heap over-read in ntfs_bdev_write() when cluster size is smaller than PAGE_SIZE - Fix use-after-free in ntfs_inode_sync_filename() when parent index inode is evicted while still holding its mrec_lock - Update resident attribute length validation to match $AttrDef - Fix refcount underflow and UAF of the global upcase table - Fix two smatch warnings * tag 'ntfs-for-7.1-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs: ntfs: restore $MFT mirror contents check ntfs: fix empty_buf and ra lifetime bugs in ntfs_empty_logfile() ntfs: validate attribute name bounds before returning it ntfs: fix MFT bitmap scan 2^32 boundary check ntfs: validate MFT attrs_offset against bytes_in_use ntfs: fix missing kstrdup() error check in ntfs_write_volume_label() ntfs: avoid leaking uninitialised bytes in new security descriptors ntfs: fix out-of-bounds write in ntfs_index_walk_down() ntfs: fix out-of-bounds write in ntfs_rl_collapse_range() merge path ntfs: fix variable dereferenced before check ni in ntfs_attr_open() ntfs: fix default_upcase refcount underflow and UAF on fs_context teardown ntfs: match ntfs_resident_attr_min_value_length with $AttrDef ntfs: avoid use-after-free of index inode in ntfs_inode_sync_filename() ntfs: fix copy length in ntfs_bdev_write() for non-page-aligned start ntfs: wait for sync mft writes to complete ntfs: capture mft mirror sync errors in ntfs_write_mft_block() ntfs: redirty folio when ntfs_write_mft_block() runs out of memory ntfs: use base mft_no when looking up base inode for extent record ntfs: fix variable dereferenced before check ni and attr in ntfs_attrlist_entry_add()
2 parents 650d213 + 2beaa98 commit 0c0b282

10 files changed

Lines changed: 149 additions & 80 deletions

File tree

fs/ntfs/attrib.c

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -583,24 +583,13 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 type)
583583
case AT_STANDARD_INFORMATION:
584584
return offsetof(struct standard_information, ver) +
585585
sizeof(((struct standard_information *)0)->ver.v1.reserved12);
586-
case AT_ATTRIBUTE_LIST:
587-
return offsetof(struct attr_list_entry, name);
588586
case AT_FILE_NAME:
589-
return offsetof(struct file_name_attr, file_name);
590-
case AT_OBJECT_ID:
591-
return sizeof(struct guid);
592-
case AT_SECURITY_DESCRIPTOR:
593-
return sizeof(struct security_descriptor_relative);
587+
return offsetof(struct file_name_attr, file_name) +
588+
sizeof(__le16) * 1;
594589
case AT_VOLUME_INFORMATION:
595590
return sizeof(struct volume_information);
596-
case AT_INDEX_ROOT:
597-
return sizeof(struct index_root);
598-
case AT_REPARSE_POINT:
599-
return offsetof(struct reparse_point, reparse_data);
600591
case AT_EA_INFORMATION:
601592
return sizeof(struct ea_information);
602-
case AT_EA:
603-
return offsetof(struct ea_attr, ea_name) + 1;
604593
default:
605594
return 0;
606595
}
@@ -672,6 +661,9 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
672661
__le16 *upcase = vol->upcase;
673662
u32 upcase_len = vol->upcase_len;
674663
unsigned int space;
664+
u16 name_offset;
665+
u32 attr_len;
666+
u32 name_size;
675667

676668
/*
677669
* Iterate over attributes in mft record starting at @ctx->attr, or the
@@ -699,6 +691,20 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
699691
return -ENOENT;
700692
if (unlikely(!a->length))
701693
break;
694+
if (a->name_length) {
695+
name_offset = le16_to_cpu(a->name_offset);
696+
attr_len = le32_to_cpu(a->length);
697+
name_size = a->name_length * sizeof(__le16);
698+
699+
if (name_offset > attr_len ||
700+
attr_len - name_offset < name_size) {
701+
ntfs_error(vol->sb,
702+
"Corrupt attribute name in MFT record %llu\n",
703+
ctx->ntfs_ino->mft_no);
704+
break;
705+
}
706+
}
707+
702708
if (type == AT_UNUSED)
703709
return 0;
704710
if (a->type != type)
@@ -712,14 +718,6 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
712718
if (a->name_length)
713719
return -ENOENT;
714720
} else {
715-
if (a->name_length && ((le16_to_cpu(a->name_offset) +
716-
a->name_length * sizeof(__le16)) >
717-
le32_to_cpu(a->length))) {
718-
ntfs_error(vol->sb, "Corrupt attribute name in MFT record %llu\n",
719-
ctx->ntfs_ino->mft_no);
720-
break;
721-
}
722-
723721
if (!ntfs_are_names_equal(name, name_len,
724722
(__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)),
725723
a->name_length, ic, upcase, upcase_len)) {
@@ -2924,12 +2922,12 @@ int ntfs_attr_open(struct ntfs_inode *ni, const __le32 type,
29242922
struct ntfs_inode *base_ni;
29252923
int err;
29262924

2927-
ntfs_debug("Entering for inode %lld, attr 0x%x.\n",
2928-
(unsigned long long)ni->mft_no, type);
2929-
29302925
if (!ni || !ni->vol)
29312926
return -EINVAL;
29322927

2928+
ntfs_debug("Entering for inode %lld, attr 0x%x.\n",
2929+
ni->mft_no, type);
2930+
29332931
if (NInoAttr(ni))
29342932
base_ni = ni->ext.base_ntfs_ino;
29352933
else

fs/ntfs/attrlist.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,14 @@ int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr)
119119
struct mft_record *ni_mrec;
120120
u8 *old_al;
121121

122-
ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n",
123-
(long long) ni->mft_no,
124-
(unsigned int) le32_to_cpu(attr->type));
125-
126122
if (!ni || !attr) {
127123
ntfs_debug("Invalid arguments.\n");
128124
return -EINVAL;
129125
}
130126

127+
ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n",
128+
ni->mft_no, (unsigned int) le32_to_cpu(attr->type));
129+
131130
ni_mrec = map_mft_record(ni);
132131
if (IS_ERR(ni_mrec)) {
133132
ntfs_debug("Invalid arguments.\n");

fs/ntfs/bdev-io.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size
9797
idx_end++;
9898

9999
for (; idx < idx_end; idx++, from = 0) {
100+
u32 len;
101+
100102
folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL);
101103
if (IS_ERR(folio)) {
102104
ntfs_error(sb, "Unable to read %ld page", idx);
@@ -105,9 +107,10 @@ int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size
105107

106108
offset = (loff_t)idx << PAGE_SHIFT;
107109
to = min_t(u32, end - offset, PAGE_SIZE);
110+
len = to - from;
108111

109-
memcpy_to_folio(folio, from, buf + buf_off, to);
110-
buf_off += to;
112+
memcpy_to_folio(folio, from, buf + buf_off, len);
113+
buf_off += len;
111114
folio_mark_uptodate(folio);
112115
folio_mark_dirty(folio);
113116
folio_put(folio);

fs/ntfs/index.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -677,11 +677,11 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl
677677

678678
static int ntfs_icx_parent_inc(struct ntfs_index_context *icx)
679679
{
680-
icx->pindex++;
681-
if (icx->pindex >= MAX_PARENT_VCN) {
680+
if (icx->pindex >= MAX_PARENT_VCN - 1) {
682681
ntfs_error(icx->idx_ni->vol->sb, "Index is over %d level deep", MAX_PARENT_VCN);
683682
return -EOPNOTSUPP;
684683
}
684+
icx->pindex++;
685685
return 0;
686686
}
687687

@@ -1970,6 +1970,7 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
19701970
{
19711971
struct index_entry *entry;
19721972
struct index_block *ib;
1973+
int err;
19731974
s64 vcn;
19741975

19751976
entry = ie;
@@ -1979,14 +1980,20 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
19791980
ib = kvzalloc(ictx->block_size, GFP_NOFS);
19801981
if (!ib)
19811982
return ERR_PTR(-ENOMEM);
1982-
/* down from level zero */
1983+
/*
1984+
* Descending from root index (level 0) to the first
1985+
* child level. is_in_root == true implies pindex == 0,
1986+
* so advance to level 1.
1987+
*/
1988+
ictx->pindex = 1;
19831989
ictx->ir = NULL;
19841990
ictx->ib = ib;
1985-
ictx->pindex = 1;
19861991
ictx->is_in_root = false;
19871992
} else {
19881993
/* down from non-zero level */
1989-
ictx->pindex++;
1994+
err = ntfs_icx_parent_inc(ictx);
1995+
if (err)
1996+
return ERR_PTR(err);
19901997
}
19911998

19921999
ictx->parent_pos[ictx->pindex] = 0;

fs/ntfs/inode.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,17 +2582,17 @@ int ntfs_inode_sync_filename(struct ntfs_inode *ni)
25822582

25832583
mutex_lock_nested(&index_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
25842584
if (NInoBeingDeleted(ni)) {
2585-
iput(index_vi);
25862585
mutex_unlock(&index_ni->mrec_lock);
2586+
iput(index_vi);
25872587
continue;
25882588
}
25892589

25902590
ictx = ntfs_index_ctx_get(index_ni, I30, 4);
25912591
if (!ictx) {
25922592
ntfs_error(sb, "Failed to get index ctx, inode %llu",
25932593
index_ni->mft_no);
2594-
iput(index_vi);
25952594
mutex_unlock(&index_ni->mrec_lock);
2595+
iput(index_vi);
25962596
continue;
25972597
}
25982598

@@ -2601,8 +2601,8 @@ int ntfs_inode_sync_filename(struct ntfs_inode *ni)
26012601
ntfs_debug("Index lookup failed, inode %llu",
26022602
index_ni->mft_no);
26032603
ntfs_index_ctx_put(ictx);
2604-
iput(index_vi);
26052604
mutex_unlock(&index_ni->mrec_lock);
2605+
iput(index_vi);
26062606
continue;
26072607
}
26082608
/* Update flags and file size. */

fs/ntfs/logfile.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,9 @@ bool ntfs_empty_logfile(struct inode *log_vi)
710710
if (unlikely(lcn == LCN_RL_NOT_MAPPED)) {
711711
vcn = rl->vcn;
712712
kvfree(empty_buf);
713+
empty_buf = NULL;
714+
kfree(ra);
715+
ra = NULL;
713716
goto map_vcn;
714717
}
715718
/* If this run is not valid abort with an error. */
@@ -753,7 +756,7 @@ bool ntfs_empty_logfile(struct inode *log_vi)
753756
} while (start < end);
754757
} while ((++rl)->vcn < end_vcn);
755758
up_write(&log_ni->runlist.lock);
756-
kfree(empty_buf);
759+
kvfree(empty_buf);
757760
kfree(ra);
758761
truncate_inode_pages(log_vi->i_mapping, 0);
759762
/* Set the flag so we do not have to do it again on remount. */

0 commit comments

Comments
 (0)