diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 0d1be4da3e4f..66ead9db26ee 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -2599,11 +2599,12 @@ static int read_next_log_rec(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) 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 (hdr_has_subnode(hdr)) { + if (has_subnode) { min_de = sizeof(struct NTFS_DE) + sizeof(u64); mask = NTFS_IE_HAS_SUBNODES; } else { @@ -2620,20 +2621,33 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) return false; } - e = Add2Ptr(hdr, de_off); + e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); for (;;) { u16 esize = le16_to_cpu(e->size); - struct NTFS_DE *next = Add2Ptr(e, esize); + u16 key_size = le16_to_cpu(e->key_size); + u16 data_size; - if (esize < min_de || PtrOffset(hdr, next) > used || + if (!IS_ALIGNED(esize, 8) || esize < min_de || (e->flags & NTFS_IE_HAS_SUBNODES) != mask) { return false; } - if (de_is_last(e)) - break; + if (size_add(de_off, esize) > used) + return false; - e = next; + if (de_is_last(e)) { + if (key_size) + return false; + + break; + } + + data_size = esize - min_de; + if (key_size > data_size) + return false; + + de_off += esize; + e = (const struct NTFS_DE *)((const u8 *)hdr + de_off); } return true; diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index d65984d9dc07..85f674e9e361 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -611,16 +611,51 @@ 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); u32 end = le32_to_cpu(hdr->used); u32 tot = le32_to_cpu(hdr->total); u32 off = le32_to_cpu(hdr->de_off); + const struct NTFS_DE *e; if (!IS_ALIGNED(off, 8) || tot > bytes || end > tot || - size_add(off, sizeof(struct NTFS_DE)) > end) { + size_add(off, min_size) > end) { /* incorrect index buffer. */ return false; } + /* Ensure every key stays inside its entry before lookup walks it. */ + e = (const struct NTFS_DE *)((const u8 *)hdr + off); + for (;;) { + u16 e_size = le16_to_cpu(e->size); + u16 key_size = le16_to_cpu(e->key_size); + u16 data_size; + + if (!IS_ALIGNED(e_size, 8) || e_size < min_size || + de_has_vcn(e) != has_subnode) { + /* incorrect index entry. */ + return false; + } + + if (size_add(off, e_size) > end) + return false; + + if (de_is_last(e)) { + if (key_size) + return false; + + break; + } + + data_size = e_size - min_size; + if (key_size > data_size) + return false; + + off += e_size; + e = (const struct NTFS_DE *)((const u8 *)hdr + off); + } + return true; }