mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
fs/ntfs3: Add more attributes checks in mi_enum_attr()
[ Upstream commit 013ff63b64 ]
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
CVE: CVE-2023-45896
Signed-off-by: Xiangyu Chen <xiangyu.chen@windriver.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
0bdf77be23
commit
3dfd727873
@@ -193,8 +193,9 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|||||||
{
|
{
|
||||||
const struct MFT_REC *rec = mi->mrec;
|
const struct MFT_REC *rec = mi->mrec;
|
||||||
u32 used = le32_to_cpu(rec->used);
|
u32 used = le32_to_cpu(rec->used);
|
||||||
u32 t32, off, asize;
|
u32 t32, off, asize, prev_type;
|
||||||
u16 t16;
|
u16 t16;
|
||||||
|
u64 data_size, alloc_size, tot_size;
|
||||||
|
|
||||||
if (!attr) {
|
if (!attr) {
|
||||||
u32 total = le32_to_cpu(rec->total);
|
u32 total = le32_to_cpu(rec->total);
|
||||||
@@ -213,6 +214,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|||||||
if (!is_rec_inuse(rec))
|
if (!is_rec_inuse(rec))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
prev_type = 0;
|
||||||
attr = Add2Ptr(rec, off);
|
attr = Add2Ptr(rec, off);
|
||||||
} else {
|
} else {
|
||||||
/* Check if input attr inside record. */
|
/* Check if input attr inside record. */
|
||||||
@@ -226,6 +228,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Overflow check. */
|
||||||
|
if (off + asize < off)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
prev_type = le32_to_cpu(attr->type);
|
||||||
attr = Add2Ptr(attr, asize);
|
attr = Add2Ptr(attr, asize);
|
||||||
off += asize;
|
off += asize;
|
||||||
}
|
}
|
||||||
@@ -245,7 +252,11 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|||||||
|
|
||||||
/* 0x100 is last known attribute for now. */
|
/* 0x100 is last known attribute for now. */
|
||||||
t32 = le32_to_cpu(attr->type);
|
t32 = le32_to_cpu(attr->type);
|
||||||
if ((t32 & 0xf) || (t32 > 0x100))
|
if (!t32 || (t32 & 0xf) || (t32 > 0x100))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* attributes in record must be ordered by type */
|
||||||
|
if (t32 < prev_type)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Check overflow and boundary. */
|
/* Check overflow and boundary. */
|
||||||
@@ -254,16 +265,15 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|||||||
|
|
||||||
/* Check size of attribute. */
|
/* Check size of attribute. */
|
||||||
if (!attr->non_res) {
|
if (!attr->non_res) {
|
||||||
|
/* Check resident fields. */
|
||||||
if (asize < SIZEOF_RESIDENT)
|
if (asize < SIZEOF_RESIDENT)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
t16 = le16_to_cpu(attr->res.data_off);
|
t16 = le16_to_cpu(attr->res.data_off);
|
||||||
|
|
||||||
if (t16 > asize)
|
if (t16 > asize)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
t32 = le32_to_cpu(attr->res.data_size);
|
if (t16 + le32_to_cpu(attr->res.data_size) > asize)
|
||||||
if (t16 + t32 > asize)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (attr->name_len &&
|
if (attr->name_len &&
|
||||||
@@ -274,21 +284,52 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
|||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check some nonresident fields. */
|
/* Check nonresident fields. */
|
||||||
if (attr->name_len &&
|
if (attr->non_res != 1)
|
||||||
le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len >
|
|
||||||
le16_to_cpu(attr->nres.run_off)) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
if (attr->nres.svcn || !is_attr_ext(attr)) {
|
t16 = le16_to_cpu(attr->nres.run_off);
|
||||||
|
if (t16 > asize)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
t32 = sizeof(short) * attr->name_len;
|
||||||
|
if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Check start/end vcn. */
|
||||||
|
if (le64_to_cpu(attr->nres.svcn) > le64_to_cpu(attr->nres.evcn) + 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data_size = le64_to_cpu(attr->nres.data_size);
|
||||||
|
if (le64_to_cpu(attr->nres.valid_size) > data_size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
alloc_size = le64_to_cpu(attr->nres.alloc_size);
|
||||||
|
if (data_size > alloc_size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
t32 = mi->sbi->cluster_mask;
|
||||||
|
if (alloc_size & t32)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!attr->nres.svcn && is_attr_ext(attr)) {
|
||||||
|
/* First segment of sparse/compressed attribute */
|
||||||
|
if (asize + 8 < SIZEOF_NONRESIDENT_EX)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
tot_size = le64_to_cpu(attr->nres.total_size);
|
||||||
|
if (tot_size & t32)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (tot_size > alloc_size)
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
if (asize + 8 < SIZEOF_NONRESIDENT)
|
if (asize + 8 < SIZEOF_NONRESIDENT)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (attr->nres.c_unit)
|
if (attr->nres.c_unit)
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (asize + 8 < SIZEOF_NONRESIDENT_EX)
|
}
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user