diff options
| -rw-r--r-- | fs/ntfs3/dir.c | 36 | ||||
| -rw-r--r-- | fs/ntfs3/file.c | 2 | ||||
| -rw-r--r-- | fs/ntfs3/frecord.c | 31 | ||||
| -rw-r--r-- | fs/ntfs3/fslog.c | 21 | ||||
| -rw-r--r-- | fs/ntfs3/fsntfs.c | 8 | ||||
| -rw-r--r-- | fs/ntfs3/index.c | 7 | ||||
| -rw-r--r-- | fs/ntfs3/inode.c | 18 | ||||
| -rw-r--r-- | fs/ntfs3/lznt.c | 4 | ||||
| -rw-r--r-- | fs/ntfs3/namei.c | 9 | ||||
| -rw-r--r-- | fs/ntfs3/ntfs_fs.h | 6 | ||||
| -rw-r--r-- | fs/ntfs3/run.c | 9 | ||||
| -rw-r--r-- | fs/ntfs3/super.c | 9 | ||||
| -rw-r--r-- | fs/ntfs3/xattr.c | 1 |
13 files changed, 100 insertions, 61 deletions
diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index 873d52233003..62482c2352ad 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -273,6 +273,12 @@ out: return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode; } +static inline bool de_fname_fits(const struct NTFS_DE *e, u32 e_size, + const struct ATTR_FILE_NAME *fname) +{ + return sizeof(struct NTFS_DE) + fname_full_size(fname) <= e_size; +} + /* * returns false if 'ctx' if full */ @@ -281,7 +287,7 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi, u8 *name, struct dir_context *ctx) { const struct ATTR_FILE_NAME *fname; - unsigned long ino; + u64 ino; int name_len; u32 dt_type; @@ -305,15 +311,13 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi, if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) return true; - if (sizeof(struct NTFS_DE) + - offsetof(struct ATTR_FILE_NAME, name) + - fname->name_len * sizeof(short) > le16_to_cpu(e->size)) + if (!de_fname_fits(e, le16_to_cpu(e->size), fname)) return true; name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name, PATH_MAX); if (name_len <= 0) { - ntfs_warn(sbi->sb, "failed to convert name for inode %lx.", + ntfs_warn(sbi->sb, "failed to convert name for inode %llx.", ino); return true; } @@ -576,6 +580,23 @@ out: return err; } +/* + * Return fname when @e passes the same checks as ntfs_dir_emit() before + * exposing an entry (valid key, non-DOS, fname fits in e->size). + */ +static inline const struct ATTR_FILE_NAME * +de_countable_fname(const struct NTFS_DE *e, u32 e_size) +{ + const struct ATTR_FILE_NAME *fname; + + fname = de_get_fname(e); + if (!fname || fname->type == FILE_NAME_DOS || + !de_fname_fits(e, e_size, fname)) + return NULL; + + return fname; +} + static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, size_t *files) { @@ -615,13 +636,10 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, if (de_is_last(e)) break; - fname = de_get_fname(e); + fname = de_countable_fname(e, e_size); if (!fname) continue; - if (fname->type == FILE_NAME_DOS) - continue; - if (is_empty) { *is_empty = false; if (!dirs && !files) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index d601f088618c..fa8e2e56ff3f 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -894,7 +894,7 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) out: inode_unlock_shared(inode); - file_accessed(iocb->ki_filp); + file_accessed(file); return err; } diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 2b49bc077558..3ad309d38fe8 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -169,7 +169,7 @@ out: int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le, struct mft_inode **mi) { - CLST rno; + u64 rno; if (!le) { *mi = &ni->mi; @@ -768,10 +768,23 @@ int ni_create_attr_list(struct ntfs_inode *ni) rs = sbi->record_size; /* - * Skip estimating exact memory requirement. - * Looks like one record_size is always enough. + * Compute the exact size of the attribute list. Each attribute in the + * record yields one ATTR_LIST_ENTRY of le_size(name_len) bytes. The + * minimum on-disk attribute is SIZEOF_RESIDENT (0x18) bytes, but an + * unnamed one expands to le_size(0) (0x20) here, so a record crafted + * with many such attributes needs more than a single record_size; the + * previous fixed kzalloc(record_size) could therefore be overflowed by + * an attacker-controlled record. */ - le = kzalloc(al_aligned(rs), GFP_NOFS); + lsize = 0; + attr = NULL; + while ((attr = mi_enum_attr(ni, &ni->mi, attr))) + lsize += le_size(attr->name_len); + + if (!lsize) + return -EINVAL; + + le = kzalloc(al_aligned(lsize), GFP_NOFS); if (!le) return -ENOMEM; @@ -781,7 +794,6 @@ int ni_create_attr_list(struct ntfs_inode *ni) attr = NULL; nb = 0; free_b = 0; - attr = NULL; for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) { sz = le_size(attr->name_len); @@ -2919,7 +2931,6 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data) break; } } - } vbo = (u64)vcn << cluster_bits; @@ -2961,8 +2972,8 @@ int ni_write_parents(struct ntfs_inode *ni, int sync) if (IS_ERR(dir)) { ntfs_inode_warn( &ni->vfs_inode, - "failed to open parent directory r=%lx to write", - (long)ino_get(&fname->home)); + "failed to open parent directory r=%llx to write", + (u64)ino_get(&fname->home)); continue; } @@ -3081,8 +3092,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, if (IS_ERR(dir)) { ntfs_inode_warn( &ni->vfs_inode, - "failed to open parent directory r=%lx to update", - (long)ino_get(&fname->home)); + "failed to open parent directory r=%llx to update", + (u64)ino_get(&fname->home)); continue; } diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index f038c799e7ac..cc24c23e4db9 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -2613,7 +2613,6 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) const bool has_subnode = hdr_has_subnode(hdr); __le16 mask; u32 min_de, de_off, used, total; - const struct NTFS_DE *e; if (has_subnode) { min_de = sizeof(struct NTFS_DE) + sizeof(u64); @@ -2632,8 +2631,8 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) return false; } - e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); for (;;) { + const struct NTFS_DE *e = Add2Ptr(hdr, de_off); u16 esize = le16_to_cpu(e->size); u16 key_size = le16_to_cpu(e->key_size); u16 data_size; @@ -2649,7 +2648,6 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) if (de_is_last(e)) { if (key_size) return false; - break; } @@ -2658,7 +2656,6 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) return false; de_off += esize; - e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); } return true; @@ -3544,8 +3541,7 @@ move_data: * bound here so the memmove cannot reach past the entry. */ if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || - le16_to_cpu(e->view.data_off) + dlen > - le16_to_cpu(e->size)) + le16_to_cpu(e->view.data_off) + dlen > le16_to_cpu(e->size)) goto dirty_vol; memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); @@ -3756,8 +3752,7 @@ move_data: /* See UpdateRecordDataRoot for the rationale. */ if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) || - le16_to_cpu(e->view.data_off) + dlen > - le16_to_cpu(e->size)) + le16_to_cpu(e->view.data_off) + dlen > le16_to_cpu(e->size)) goto dirty_vol; memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); @@ -4653,11 +4648,11 @@ copy_lcns: } /* - * find_dp() only validates that target_vcn is the first - * cluster covered by dp. The walk through lrh->lcns_follow - * further entries must stay within the allocated - * dp->page_lcns[] array, which is sized by dp->lcns_follow. - */ + * find_dp() only validates that target_vcn is the first + * cluster covered by dp. The walk through lrh->lcns_follow + * further entries must stay within the allocated + * dp->page_lcns[] array, which is sized by dp->lcns_follow. + */ if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 > le32_to_cpu(dp->lcns_follow)) { err = -EINVAL; diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index bc7469d0a34d..7c4db816c43d 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2302,8 +2302,8 @@ int ntfs_reparse_init(struct ntfs_sb_info *sbi) goto out; } - root_r = resident_data(attr); - if (root_r->type != ATTR_ZERO || + root_r = resident_data_ex(attr, sizeof(struct INDEX_ROOT)); + if (!root_r || root_r->type != ATTR_ZERO || root_r->rule != NTFS_COLLATION_TYPE_UINTS) { err = -EINVAL; goto out; @@ -2340,8 +2340,8 @@ int ntfs_objid_init(struct ntfs_sb_info *sbi) goto out; } - root = resident_data(attr); - if (root->type != ATTR_ZERO || + root = resident_data_ex(attr, sizeof(struct INDEX_ROOT)); + if (!root || root->type != ATTR_ZERO || root->rule != NTFS_COLLATION_TYPE_UINTS) { err = -EINVAL; goto out; diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 2b439ac04356..4afacfc6ff89 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -612,8 +612,8 @@ static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr, static bool index_hdr_check(const struct INDEX_HDR *hdr, u32 bytes) { const bool has_subnode = hdr_has_subnode(hdr); - const u16 min_size = sizeof(struct NTFS_DE) + - (has_subnode ? sizeof(u64) : 0); + const u16 min_size = + sizeof(struct NTFS_DE) + (has_subnode ? sizeof(u64) : 0); u32 end = le32_to_cpu(hdr->used); u32 tot = le32_to_cpu(hdr->total); u32 off = le32_to_cpu(hdr->de_off); @@ -2131,8 +2131,7 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, if (err) return ERR_PTR(err); - r = indx_find_buffer(indx, ni, root, vbn, n, - depth + 1); + r = indx_find_buffer(indx, ni, root, vbn, n, depth + 1); if (r) return r; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 0c9bd669117d..4ecd7ebaa655 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -36,7 +36,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, bool is_match = false; bool is_root = false; bool is_dir; - unsigned long ino = inode->i_ino; + u64 ino = inode->i_ino; u32 rp_fa = 0, asize, t32; u16 roff, rsize, names = 0, links = 0; const struct ATTR_FILE_NAME *fname = NULL; @@ -79,7 +79,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, ; } else if (ref->seq != rec->seq) { err = -EINVAL; - ntfs_err(sb, "MFT: r=%lx, expect seq=%x instead of %x!", ino, + ntfs_err(sb, "MFT: r=%llx, expect seq=%x instead of %x!", ino, le16_to_cpu(ref->seq), le16_to_cpu(rec->seq)); goto out; } else if (!is_rec_inuse(rec)) { @@ -127,10 +127,16 @@ next_attr: if (le && le->vcn) { /* This is non primary attribute segment. Ignore if not MFT. */ - if (ino != MFT_REC_MFT || attr->type != ATTR_DATA) + if (ino != MFT_REC_MFT) + goto next_attr; + + if (attr->type == ATTR_DATA) + run = &ni->file.run; + else if (attr->type == ATTR_BITMAP) + run = &sbi->mft.bitmap.run; + else goto next_attr; - run = &ni->file.run; asize = le32_to_cpu(attr->size); goto attr_unpack_run; } @@ -606,15 +612,17 @@ static void ntfs_iomap_read_end_io(struct bio *bio) } static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, - struct iomap_read_folio_ctx *ctx) + struct iomap_read_folio_ctx *ctx) { iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io); } +// clang-format off static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { .read_folio_range = iomap_bio_read_folio_range, .submit_read = ntfs_iomap_bio_submit_read, }; +// clang-format on static int ntfs_read_folio(struct file *file, struct folio *folio) { diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c index f818d9785004..5dcb7674790c 100644 --- a/fs/ntfs3/lznt.c +++ b/fs/ntfs3/lznt.c @@ -240,8 +240,10 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, if (up - unc > LZNT_CHUNK_SIZE) return -EINVAL; /* Correct index */ - while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up) + while (index < ARRAY_SIZE(s_max_off) - 1 && + unc + s_max_off[index] < up) { index += 1; + } /* Check the current flag for zero. */ if (!(ch & (1 << bit))) { diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index c59de5f2fa97..937d349e841d 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -22,7 +22,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, { int err; struct NTFS_DE *e = buf; - u16 data_size; + u16 data_size, real_size, aligned_size; struct ATTR_FILE_NAME *fname = (struct ATTR_FILE_NAME *)(e + 1); #ifndef CONFIG_NTFS3_64BIT_CLUSTER @@ -53,7 +53,12 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, fname->type = FILE_NAME_POSIX; data_size = fname_full_size(fname); - e->size = cpu_to_le16(ALIGN(data_size, 8) + sizeof(struct NTFS_DE)); + real_size = data_size + sizeof(struct NTFS_DE); + aligned_size = ALIGN(data_size, 8) + sizeof(struct NTFS_DE); + if (aligned_size > real_size) + memset((char *)buf + real_size, 0, aligned_size - real_size); + + e->size = cpu_to_le16(aligned_size); e->key_size = cpu_to_le16(data_size); e->flags = 0; e->res = 0; diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index d98d7e474476..6bd1a439e92f 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -401,7 +401,7 @@ struct ntfs_inode { struct rw_semaphore run_lock; /* Unpacked runs from just one record. */ struct runs_tree run; - /* + /* * Pairs [vcn, len] for all delay allocated clusters. * Normal file always contains delayed clusters in one fragment. * TODO: use 2 CLST per pair instead of 3. @@ -886,8 +886,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, #else #define run_unpack_ex run_unpack #endif -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, - u64 *highest_vcn); +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn); int run_clone(const struct runs_tree *run, struct runs_tree *new_run); bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done); CLST run_len(const struct runs_tree *run); diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 3ebf0154eda3..6e3ef89fc666 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1265,8 +1265,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Return the highest vcn from a mapping pairs array * it used while replaying log file. */ -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, - u64 *highest_vcn) +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn) { const u8 *run_last = run_buf + run_buf_size; u64 vcn64 = vcn; @@ -1279,7 +1279,7 @@ int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, if (size_size > 8 || offset_size > 8) return -EINVAL; - if (run_buf + size_size + offset_size > run_last) + if (run_buf + size_size + offset_size > run_last) return -EINVAL; len = run_unpack_s64(run_buf, size_size, 0); @@ -1357,7 +1357,8 @@ bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done) if (r_end > end) { /* Remove a middle part, split. */ CLST tail_lcn = r->lcn == SPARSE_LCN ? - SPARSE_LCN : (r->lcn + (end - r->vcn)); + SPARSE_LCN : + (r->lcn + (end - r->vcn)); *done += len; r->len = d; diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 3305fe406cb2..446b37f0de79 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -1458,7 +1458,10 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) Add2Ptr(a, roff), le32_to_cpu(a->size) - roff); if (err < 0) { - ntfs_err(sb, "Failed to unpack $MFT bitmap extent (%d).", err); + ntfs_err( + sb, + "Failed to unpack $MFT bitmap extent (%d).", + err); goto put_inode_out; } err = 0; @@ -1866,8 +1869,7 @@ static int ntfs_init_fs_context(struct fs_context *fc) /* Default options. */ opts->fs_uid = current_uid(); opts->fs_gid = current_gid(); - opts->fs_fmask_inv = ~current_umask(); - opts->fs_dmask_inv = ~current_umask(); + opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask(); opts->prealloc = 1; #ifdef CONFIG_NTFS3_FS_POSIX_ACL @@ -1928,7 +1930,6 @@ static struct file_system_type ntfs_fs_type = { .kill_sb = ntfs3_kill_sb, .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; - // clang-format on static int __init init_ntfs_fs(void) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 04814dd29375..7a81369a1173 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -660,7 +660,6 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, inode->i_mode = old_mode; goto out; } - inode->i_mode = mode; } set_cached_acl(inode, type, acl); inode_set_ctime_current(inode); |
