Skip to content

Commit 0e8235d

Browse files
fs/ntfs3: Check fields while reading
Added new functions index_hdr_check and index_buf_check. Now we check all stuff for correctness while reading from disk. Also fixed bug with stale nfs data. Reported-by: van fantasy <[email protected]> Signed-off-by: Konstantin Komarov <[email protected]>
1 parent 6f80ed1 commit 0e8235d

File tree

5 files changed

+164
-58
lines changed

5 files changed

+164
-58
lines changed

fs/ntfs3/index.c

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -605,11 +605,58 @@ static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr,
605605
return e;
606606
}
607607

608+
/*
609+
* index_hdr_check
610+
*
611+
* return true if INDEX_HDR is valid
612+
*/
613+
static bool index_hdr_check(const struct INDEX_HDR *hdr, u32 bytes)
614+
{
615+
u32 end = le32_to_cpu(hdr->used);
616+
u32 tot = le32_to_cpu(hdr->total);
617+
u32 off = le32_to_cpu(hdr->de_off);
618+
619+
if (!IS_ALIGNED(off, 8) || tot > bytes || end > tot ||
620+
off + sizeof(struct NTFS_DE) > end) {
621+
/* incorrect index buffer. */
622+
return false;
623+
}
624+
625+
return true;
626+
}
627+
628+
/*
629+
* index_buf_check
630+
*
631+
* return true if INDEX_BUFFER seems is valid
632+
*/
633+
static bool index_buf_check(const struct INDEX_BUFFER *ib, u32 bytes,
634+
const CLST *vbn)
635+
{
636+
const struct NTFS_RECORD_HEADER *rhdr = &ib->rhdr;
637+
u16 fo = le16_to_cpu(rhdr->fix_off);
638+
u16 fn = le16_to_cpu(rhdr->fix_num);
639+
640+
if (bytes <= offsetof(struct INDEX_BUFFER, ihdr) ||
641+
rhdr->sign != NTFS_INDX_SIGNATURE ||
642+
fo < sizeof(struct INDEX_BUFFER)
643+
/* Check index buffer vbn. */
644+
|| (vbn && *vbn != le64_to_cpu(ib->vbn)) || (fo % sizeof(short)) ||
645+
fo + fn * sizeof(short) >= bytes ||
646+
fn != ((bytes >> SECTOR_SHIFT) + 1)) {
647+
/* incorrect index buffer. */
648+
return false;
649+
}
650+
651+
return index_hdr_check(&ib->ihdr,
652+
bytes - offsetof(struct INDEX_BUFFER, ihdr));
653+
}
654+
608655
void fnd_clear(struct ntfs_fnd *fnd)
609656
{
610657
int i;
611658

612-
for (i = 0; i < fnd->level; i++) {
659+
for (i = fnd->level - 1; i >= 0; i--) {
613660
struct indx_node *n = fnd->nodes[i];
614661

615662
if (!n)
@@ -819,9 +866,16 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi,
819866
u32 t32;
820867
const struct INDEX_ROOT *root = resident_data(attr);
821868

869+
t32 = le32_to_cpu(attr->res.data_size);
870+
if (t32 <= offsetof(struct INDEX_ROOT, ihdr) ||
871+
!index_hdr_check(&root->ihdr,
872+
t32 - offsetof(struct INDEX_ROOT, ihdr))) {
873+
goto out;
874+
}
875+
822876
/* Check root fields. */
823877
if (!root->index_block_clst)
824-
return -EINVAL;
878+
goto out;
825879

826880
indx->type = type;
827881
indx->idx2vbn_bits = __ffs(root->index_block_clst);
@@ -833,27 +887,34 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi,
833887
if (t32 < sbi->cluster_size) {
834888
/* Index record is smaller than a cluster, use 512 blocks. */
835889
if (t32 != root->index_block_clst * SECTOR_SIZE)
836-
return -EINVAL;
890+
goto out;
837891

838892
/* Check alignment to a cluster. */
839893
if ((sbi->cluster_size >> SECTOR_SHIFT) &
840894
(root->index_block_clst - 1)) {
841-
return -EINVAL;
895+
goto out;
842896
}
843897

844898
indx->vbn2vbo_bits = SECTOR_SHIFT;
845899
} else {
846900
/* Index record must be a multiple of cluster size. */
847901
if (t32 != root->index_block_clst << sbi->cluster_bits)
848-
return -EINVAL;
902+
goto out;
849903

850904
indx->vbn2vbo_bits = sbi->cluster_bits;
851905
}
852906

853907
init_rwsem(&indx->run_lock);
854908

855909
indx->cmp = get_cmp_func(root);
856-
return indx->cmp ? 0 : -EINVAL;
910+
if (!indx->cmp)
911+
goto out;
912+
913+
return 0;
914+
915+
out:
916+
ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
917+
return -EINVAL;
857918
}
858919

859920
static struct indx_node *indx_new(struct ntfs_index *indx,
@@ -1011,6 +1072,13 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
10111072
goto out;
10121073

10131074
ok:
1075+
if (!index_buf_check(ib, bytes, &vbn)) {
1076+
ntfs_inode_err(&ni->vfs_inode, "directory corrupted");
1077+
ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR);
1078+
err = -EINVAL;
1079+
goto out;
1080+
}
1081+
10141082
if (err == -E_NTFS_FIXUP) {
10151083
ntfs_write_bh(ni->mi.sbi, &ib->rhdr, &in->nb, 0);
10161084
err = 0;
@@ -1601,9 +1669,9 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
16011669

16021670
if (err) {
16031671
/* Restore root. */
1604-
if (mi_resize_attr(mi, attr, -ds_root))
1672+
if (mi_resize_attr(mi, attr, -ds_root)) {
16051673
memcpy(attr, a_root, asize);
1606-
else {
1674+
} else {
16071675
/* Bug? */
16081676
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
16091677
}

fs/ntfs3/inode.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
8181
le16_to_cpu(ref->seq), le16_to_cpu(rec->seq));
8282
goto out;
8383
} else if (!is_rec_inuse(rec)) {
84-
err = -EINVAL;
84+
err = -ESTALE;
8585
ntfs_err(sb, "Inode r=%x is not in use!", (u32)ino);
8686
goto out;
8787
}
@@ -92,8 +92,10 @@ static struct inode *ntfs_read_mft(struct inode *inode,
9292
goto out;
9393
}
9494

95-
if (!is_rec_base(rec))
96-
goto Ok;
95+
if (!is_rec_base(rec)) {
96+
err = -EINVAL;
97+
goto out;
98+
}
9799

98100
/* Record should contain $I30 root. */
99101
is_dir = rec->flags & RECORD_FLAG_DIR;
@@ -466,7 +468,6 @@ static struct inode *ntfs_read_mft(struct inode *inode,
466468
inode->i_flags |= S_NOSEC;
467469
}
468470

469-
Ok:
470471
if (ino == MFT_REC_MFT && !sb->s_root)
471472
sbi->mft.ni = NULL;
472473

@@ -520,6 +521,9 @@ struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref,
520521
_ntfs_bad_inode(inode);
521522
}
522523

524+
if (IS_ERR(inode) && name)
525+
ntfs_set_state(sb->s_fs_info, NTFS_DIRTY_ERROR);
526+
523527
return inode;
524528
}
525529

@@ -1660,10 +1664,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
16601664
ntfs_remove_reparse(sbi, IO_REPARSE_TAG_SYMLINK, &new_de->ref);
16611665

16621666
out5:
1663-
if (S_ISDIR(mode) || run_is_empty(&ni->file.run))
1664-
goto out4;
1665-
1666-
run_deallocate(sbi, &ni->file.run, false);
1667+
if (!S_ISDIR(mode))
1668+
run_deallocate(sbi, &ni->file.run, false);
16671669

16681670
out4:
16691671
clear_rec_inuse(rec);

fs/ntfs3/ntfs_fs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -797,12 +797,12 @@ int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf,
797797
u32 run_buf_size, CLST *packed_vcns);
798798
int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
799799
CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf,
800-
u32 run_buf_size);
800+
int run_buf_size);
801801

802802
#ifdef NTFS3_CHECK_FREE_CLST
803803
int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
804804
CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf,
805-
u32 run_buf_size);
805+
int run_buf_size);
806806
#else
807807
#define run_unpack_ex run_unpack
808808
#endif

fs/ntfs3/run.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -919,12 +919,15 @@ int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf,
919919
*/
920920
int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
921921
CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf,
922-
u32 run_buf_size)
922+
int run_buf_size)
923923
{
924924
u64 prev_lcn, vcn64, lcn, next_vcn;
925925
const u8 *run_last, *run_0;
926926
bool is_mft = ino == MFT_REC_MFT;
927927

928+
if (run_buf_size < 0)
929+
return -EINVAL;
930+
928931
/* Check for empty. */
929932
if (evcn + 1 == svcn)
930933
return 0;
@@ -1046,7 +1049,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
10461049
*/
10471050
int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
10481051
CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf,
1049-
u32 run_buf_size)
1052+
int run_buf_size)
10501053
{
10511054
int ret, err;
10521055
CLST next_vcn, lcn, len;

0 commit comments

Comments
 (0)