summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/ntfs3/dir.c36
-rw-r--r--fs/ntfs3/file.c2
-rw-r--r--fs/ntfs3/frecord.c31
-rw-r--r--fs/ntfs3/fslog.c21
-rw-r--r--fs/ntfs3/fsntfs.c8
-rw-r--r--fs/ntfs3/index.c7
-rw-r--r--fs/ntfs3/inode.c18
-rw-r--r--fs/ntfs3/lznt.c4
-rw-r--r--fs/ntfs3/namei.c9
-rw-r--r--fs/ntfs3/ntfs_fs.h6
-rw-r--r--fs/ntfs3/run.c9
-rw-r--r--fs/ntfs3/super.c9
-rw-r--r--fs/ntfs3/xattr.c1
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);